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方法:

  1. 为任何数据类型创建通用集合映射器
  2. [可选]创建一个在不同数据类型之间转换的方法库(并覆盖该方法)
  3. 使用该库

实施:

 // 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 it; public MyIterator(Iterator 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 

但我怀疑这种方法会为你节省多少。