我正在编写一个适配器框架,我需要将对象列表从一个类转换为另一个类。我可以迭代源列表来执行此操作,如
所示 的最佳方法但是,我想知道是否有一种方法可以在迭代目标列表时即时执行此操作,这样我就不必迭代列表两次。
Java 8 方式:
List<String> original = ...;
List<Wrapper> converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
假设
Wrapper
类有一个接受 String
的构造函数。
我对该问题的答案适用于您的情况:
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);
这很有用,因为您每次都必须使用转换来封装流程。 因此,您可以非常轻松地创建集合映射器库。
如果您尝试获取列表中的元素列表,请使用以下代码。这里的对象列表包含属性名称,下面从该列表中获取名称列表
inputList.stream().map(p -> p.getName()).collect(Collectors.toList());
您可以编写一个映射迭代器来装饰现有迭代器并在其上应用函数。在这种情况下,该函数将对象从一种类型“即时”转换为另一种类型。
类似这样的:
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());
非常简单。不是吗?
这可能是一个解决方案 --> 通过使用地图
List<Employee> employee = Arrays.asList(new Emp(1, 100), new Emp(2, 200), new Emp(3, 300));
List<Employee> employeS = employee.stream()
.map(emp -> new Emp(emp.getId(), emp.getSalary * 100))
.collect(Collectors.toList());
employeS .stream() .forEach(s -> System.out.println("Id :" + s.getId() + " Salary :" + s.getSalary()));
该问题不会重复列表两次。它只迭代一次,并且是迄今为止唯一已知的方法。
您也可以在 google-collections 的 commons-collections 中使用一些转换器类,但它们在幕后都做同样的事情:)以下是一种方法
CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());
好吧,您可以创建自己的迭代器包装类来执行此操作。 但我怀疑这样做是否能节省很多钱。
这是一个简单的示例,它将任何迭代器包装为 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();
}
}
我认为您要么必须创建一个自定义列表(实现列表接口)或一个自定义迭代器。例如:
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
但我怀疑这种方法能否为你节省很多。
这是一种即时方法。 (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();
}
}
}
这种转换复杂对象类型的方式非常简单直观,以后可以轻松修改。
class Model {
private int start;
private int end;
private String type;
//other variables
//acccessors , constructors etc
}**
Say we need to convert this object into another one
listed below. Add a constructor with Model object
class TransformModel {
private int start;
private int end;
private String type;
TransformeModel(Model model) {
this.start = model.start;
this.end = model.end;
this.type = model.type;
}**
public List<TransformModel> convertFromModel(List<Model> models) {
return models.stream().map(TransformModel::new).collect(Collectors.toList());
}
}
为了构建 Alexey Malev 的第一个答案,他们的答案对我有用,但我使用了 Java 21 版本:
List<YourClass> yourClass = myClass.stream().map(YourClass::new).toList();
这是为了将 MyClass 类型的 List 转换为 YourClass 类型的 List,并且列表中的内容相同。