Java:将一种元素类型的列表转换为另一种类型的列表
我正在编写一个适配器框架,我需要将对象列表从一个类转换为另一个类。 我可以遍历源列表来执行此操作
Java:将List 转换为List 的最佳方法
但是,我想知道是否有一种方法可以在迭代目标列表时动态执行此操作,因此我不必遍历列表两次。
我对这个问题的回答适用于你的情况:
import com.google.common.collect.Lists; import com.google.common.base.Functions List integers = Arrays.asList(1, 2, 3, 4); List strings = Lists.transform(integers, Functions.toStringFunction());
转换后的列表是原始集合的视图,因此在访问目标List
时会发生转换。
作为迭代器模式的替代方法,您可以使用抽象通用映射器类,并仅覆盖transform方法:
- 为任何数据类型创建通用集合映射器
- [可选]创建一个在不同数据类型之间转换的方法库(并覆盖该方法)
- 使用该库
实施:
// Generic class to transform collections public abstract class CollectionTransformer { abstract F transform(E e); public List transform(List list) { List newList = new ArrayList (); 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 mapIntegerToStringCollection(List list) { CollectionTransformer transformer = new CollectionTransformer() { @Override String transform(Integer e) { return e.toString(); } }; return transformer.transform(list); } // Example Usage List integers = Arrays.asList(1,2); List strings = mapIntegerToStringCollection(integers);
这将是有用的,你必须每次都使用转换,封装过程。 因此,您可以非常轻松地创建集合映射器库。
Java 8方式:
List original = ...; List converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
假设Wrapper
类有一个接受String
的构造函数。
您可以编写一个映射迭代器来装饰现有迭代器并在其上应用函数。 在这种情况下,该函数“在运行中”将对象从一种类型转换为另一种类型。
像这样的东西:
import java.util.*; abstract class Transformer implements Iterable, Iterator { public abstract U apply(T object); final Iterator source; Transformer(Iterable 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 iterator() { return this; } } public class TransformingIterator { public static void main(String args[]) { List list = Arrays.asList("1", "2", "3"); Iterable it = new Transformer(list) { @Override public Integer apply(String s) { return Integer.parseInt(s); } }; for (int i : it) { System.out.println(i); } } }
Lambdaj允许以非常简单和可读的方式执行此操作。 例如,假设您有一个Integer列表,并且您想要在相应的String表示中转换它们,您可以编写类似的东西;
List ints = asList(1, 2, 3, 4); Iterator stringIterator = convertIterator(ints, new Converter { public String convert(Integer i) { return Integer.toString(i); } });
只有在迭代结果时,Lambdaj才会应用转换函数。 还有一种更简洁的方法来使用相同的function。 下一个示例假设您有一个具有name属性的人员列表,并且您希望在人名的迭代器中转换该列表。
Iterator namesIterator = convertIterator(persons, on(Person.class).getName());
满容易。 不是吗?
那个问题没有两次遍历列表。 它只迭代一次,到目前为止是唯一已知的方法。
你也可以在goons-collections的公共集合中使用一些变换器类,但它们都在引擎盖下做同样的事情:)以下是一种方式
CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());
好吧,您可以创建自己的迭代器包装类来执行此操作。 但我怀疑你这样做可以节省多少钱。
这是一个将任何迭代器包装到String迭代器的简单示例,使用Object.toString()进行映射。
public MyIterator implements Iterator { 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(); } }
我认为您要么必须创建自定义List(实现List接口)或自定义Iterator。 例如:
ArrayList targetList = new ArrayList (); ConvertingIterator iterator = new ConvertingIterator (targetList); // and here you would have to use a custom List implementation as a source List // using the Iterator created above
但我怀疑这种方法会为你节省多少。