Java: преобразование списков одного типа элементов в список другого типа

Я пишу инфраструктуру адаптера, где мне нужно преобразовать список объектов из одного класса в другой. Я могу выполнить итерацию через исходный список, чтобы сделать это, как в

Java: лучший способ преобразования List <Integer> для списка <String>

Однако мне интересно, есть ли способ сделать это "на лету", когда повторяется список целей, поэтому мне не нужно повторять этот список дважды.

Ответ 1

Мой ответ на этот вопрос относится к вашему делу:

import com.google.common.collect.Lists;
import com.google.common.base.Functions

List<Integer> integers = Arrays.asList(1, 2, 3, 4);

List<String> strings = Lists.transform(integers, Functions.toStringFunction());

Преобразованный список представляет собой представление исходной коллекции, поэтому преобразование происходит, когда обращается к пункту назначения List.

Ответ 2

В качестве альтернативы шаблону итератора вы можете использовать абстрактный общий класс сопоставления и только переопределить метод преобразования:

  • создать универсальный сборщик для любого типа данных
  • [необязательно] создать библиотеку методов, которые преобразуются между разными типами данных (и переопределяют метод)
  • используйте эту библиотеку

реализация:

// Generic class to transform collections
public abstract class CollectionTransformer<E, F> {

    abstract F transform(E e);

    public List<F> transform(List<E> list) {
        List<F> newList = new ArrayList<F>();
        for (E e : list) {
            newList.add(transform(e));
        }
        return newList;
    }
}

// Method that transform Integer to String
// this override the transform method to specify the transformation
public static List<String> mapIntegerToStringCollection(List<Integer> list) {

    CollectionTransformer transformer = new CollectionTransformer<Integer, String>() {
        @Override  
        String transform(Integer e) {
            return e.toString();
        }
    };
    return transformer.transform(list);
}

// Example Usage
List<Integer> integers = Arrays.asList(1,2);
List<String> strings = mapIntegerToStringCollection(integers);

Это было бы полезно, если вам нужно каждый раз использовать преобразования, инкапсулируя процесс. Таким образом, вы можете легко сделать библиотеку собирателей.

Ответ 3

Java 8-way:

List<String> original = ...;
List<Wrapper> converted = original.stream().map(Wrapper::new).collect(Collectors.toList());

Предполагая, что класс Wrapper имеет конструктор, принимающий String.

Ответ 4

Вы можете написать итератор отображения, который украсит существующий итератор и применит к нему функцию. В этом случае функция преобразует объекты из одного типа в другой "на лету".

Что-то вроде этого:

import java.util.*;

abstract class Transformer<T, U> implements Iterable<U>, Iterator<U> {
    public abstract U apply(T object);  

    final Iterator<T> source;       
    Transformer(Iterable<T> source)    { this.source = source.iterator(); }

    @Override public boolean hasNext() { return source.hasNext(); }
    @Override public U next()          { return apply(source.next()); }
    @Override public void remove()     { source.remove(); } 

    public Iterator<U> iterator()      { return this; }
}

public class TransformingIterator { 
    public static void main(String args[]) {
        List<String> list = Arrays.asList("1", "2", "3");
        Iterable<Integer> it = new Transformer<String, Integer>(list) {
            @Override public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };
        for (int i : it) {
            System.out.println(i);
        }
    }
}

Ответ 5

Lambdaj позволяет сделать это очень простым и понятным способом. Например, предположим, что у вас есть список Integer и вы хотите преобразовать их в соответствующее строковое представление, вы могли бы написать что-то подобное:

List<Integer> ints = asList(1, 2, 3, 4);
Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
    public String convert(Integer i) { return Integer.toString(i); }
});

Lambdaj применяет функцию преобразования только во время повторения результата. Существует также более сжатый способ использования одной и той же функции. В следующем примере предполагается, что у вас есть список лиц с свойством name и вы хотите преобразовать этот список в итератор имен людей.

Iterator<String> namesIterator = convertIterator(persons, on(Person.class).getName());

Довольно легко. Не правда ли?

Ответ 6

Этот вопрос не перебирает список дважды. Он просто итерационно один раз и является единственным известным методом.

Также вы можете использовать некоторые классы трансформаторов в коллекциях коллекций google-коллекций, но все они делают то же самое под капотом:) следующим образом:

CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());

Ответ 7

Ну, вы можете создать свой собственный класс оболочки итератора, чтобы сделать это. Но я сомневаюсь, что вы бы сэкономили, сделав это.

Вот простой пример, который переносит любой итератор на итератор String, используя Object.toString() для выполнения сопоставления.

public MyIterator implements Iterator<String> {

    private Iterator<? extends Object> it;

    public MyIterator(Iterator<? extends Object> it) {
        this.it = it;
    }

    public boolean hasNext() {
        return it.hasNext();
    }

    public String next() {
        return it.next().toString();
    }

    public void remove() {
        it.remove();
    }
}

Ответ 8

Я думаю, вам нужно будет создать собственный список (реализация интерфейса List) или пользовательский Iterator. Например:

ArrayList<String> targetList = new ArrayList<String>();
ConvertingIterator<String> iterator = new ConvertingIterator<String>(targetList);
// and here you would have to use a custom List implementation as a source List
// using the Iterator created above

Но я сомневаюсь, что этот подход сэкономит вам много.