オブジェクトのリストをあるクラスから別のクラスに変換する必要があるアダプタフレームワークを書いています。ソースリストを繰り返し処理して、次のようにすることができます。
Java:List <Integer>をList <String>に変換する最良の方法
ただし、ターゲットリストが繰り返されているときに、その場でこれを行う方法があるかどうか疑問に思っているので、リストを2回繰り返す必要はありません。
Java 8の方法:
List<String> original = ...;
List<Wrapper> converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
Wrapper
クラスにString
を受け入れるコンストラクターがあると仮定します。
私の answer その質問はあなたのケースに当てはまります:
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
にアクセスすると変換が行われます。
イテレーターパターンの代わりに、抽象ジェネリックマッパークラスを使用して、変換メソッドのみをオーバーライドできます。
実装:
// 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);
これは、プロセスをカプセル化して、毎回変換を使用する必要がある場合に役立ちます。したがって、コレクションマッパーのライブラリを非常に簡単にすることができます。
既存のイテレータを装飾し、それに関数を適用するマッピングイテレータを作成できます。この場合、関数はオブジェクトをあるタイプから別のタイプに「オンザフライ」で変換します。
このようなもの:
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);
}
}
}
Lambdaj は、非常にシンプルで読みやすい方法でそれを行うことができます。たとえば、整数のリストがあり、それらを対応する文字列表現に変換したい場合は、次のように記述できます。
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());
とても簡単。そうですね。
これがオンザフライのアプローチです。 (jdkにはすでにこのようなものがあるはずです。私はそれを見つけることができません。)
package com.gnahraf.util;
import Java.util.AbstractList;
import Java.util.List;
import Java.util.Objects;
import Java.util.function.Function;
/**
*
*/
public class Lists {
private Lists() { }
public static <U,V> List<V> transform(List<U> source, Function<U, V> mapper) {
return new ListView<U, V>(source, mapper);
}
protected static class ListView<U, V> extends AbstractList<V> {
private final List<U> source;
private final Function<U, V> mapper;
protected ListView(List<U> source, Function<U, V> mapper) {
this.source = Objects.requireNonNull(source, "source");
this.mapper = Objects.requireNonNull(mapper, "mapper");
}
@Override
public V get(int index) {
return mapper.apply(source.get(index));
}
@Override
public int size() {
return source.size();
}
}
}
その質問は、リストを2回繰り返すことはありません。それは一度だけ繰り返され、断然唯一の既知の方法です。
また、commons-collections of google-collectionsでいくつかのトランスフォーマークラスを使用することもできますが、それらはすべて内部で同じことを行います:)以下は1つの方法です
CollectionUtils.collect(collectionOfIntegers, new org.Apache.commons.collections.functors.StringValueTransformer());
これを行うために、独自のイテレータラッパークラスを作成できます。しかし、これを行うことで多くの節約になるとは思えません。
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();
}
}
カスタムリスト(リストインターフェイスを実装)またはカスタムイテレータを作成する必要があると思います。例えば:
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
しかし、このアプローチがあなたを大いに救うとは思えません。