Java – 将对象列表映射到包含其属性属性值的列表

我将ViewValue类定义如下:

class ViewValue { private Long id; private Integer value; private String description; private View view; private Double defaultFeeRate; // getters and setters for all properties } 

在我的代码中的某处,我需要将ViewValue实例列表转换为包含来自相应ViewValue的id字段值的列表。

我使用foreach循环来做到这一点:

 List toIdsList(List viewValues) { List ids = new ArrayList(); for (ViewValue viewValue : viewValues) { ids.add(viewValue.getId()); } return ids; 

}

有没有更好的方法来解决这个问题?

编辑:这个答案是基于这样的想法,你需要为代码中的其他实体和其他属性做类似的事情。 如果您需要通过ID将ViewValues列表转换为Longs列表,那么请坚持使用原始代码。 但是,如果您想要一个更可重用的解决方案,请继续阅读……

我会声明投影的界面,例如

 public interface Function { public Result apply(Arg arg); } 

然后你可以编写一个通用的转换方法:

 public  List convertAll(List source, Function projection) { ArrayList results = new ArrayList(); for (Source element : source) { results.add(projection.apply(element)); } return results; } 

然后你可以像这样定义简单的投影:

 private static final Function ID_PROJECTION = new Function() { public Long apply(ViewValue x) { return x.getId(); } }; 

并像这样应用它:

 List ids = convertAll(values, ID_PROJECTION); 

(显然使用K&R支撑和更长的线条使投影声明更短:)

坦率地说,所有这些对于lambda表达式来说会更好,但没关系……

你可以使用Commons BeanUtils和Collections在一行中完成:
(为什么在别人为你做的时候编写自己的代码?)

 import org.apache.commons.beanutils.BeanToPropertyValueTransformer; import org.apache.commons.collections.CollectionUtils; ... List ids = (List) CollectionUtils.collect(viewValues, new BeanToPropertyValueTransformer("id")); 

使用谷歌collections。 例:

  Function transform = new Function() { @Override public Long apply(ViewValue from) { return from.getId(); } }; List list = Lists.newArrayList(); List idsList = Lists.transform(list, transform); 

更新:

在Java 8上,您不需要Guava。 您可以:

 import com.example.ViewValue; import java.util.ArrayList; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; Function transform = ViewValue::getId; List source = new ArrayList<>(); List result = source.stream().map(transform).collect(Collectors.toList()); 

要不就:

 List source= new ArrayList<>(); List result = source.stream().map(ViewValue::getId).collect(Collectors.toList()); 

NEXT UPDATE(Javaslang之后的最后一个改名为Vavr):

目前值得一提的是Javaslang库( http://www.javaslang.io/)Vavr库( http://www.vavr.io/的解决方案。 让我们假设我们的列表包含真正的对象:

 List source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2)); 

我们可以使用Javaslang库中的List类进行转换(从长远来看,收集不方便):

 List result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList(); 

但是你会看到只有Javaslang列表的力量:

 io.vavr.collection.List source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3)); io.vavr.collection.List res = source.map(ViewValue::getId); 

我鼓励在该库上查看可用的集合和新类型(我特别喜欢Try类型)。 您可以在以下地址找到文档: http//www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/

PS。 由于Oracle和名称中的“Java”字,他们必须将库名从javaslang更改为其他名称。 他们决定去Vavr。

我们可以使用java 8在一行代码中完成

 List ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList()); 

有关更多信息: Java 8 – Streams

我为这个用例实现了一个小函数库。 其中一种方法有这个签名:

  List mapToProperty(List objectList, String property, Class returnType) 

它接受字符串并使用reflection来创建对属性的调用,然后返回由objectList支持的List,其中使用此属性调用实现get和iterator。

mapToProperty函数是根据一般的map函数实现的,该函数将Function作为映射器,就像另一篇文章所描述的那样。 非常有用。

我建议你阅读基本的函数编程,特别是看看Functors(实现map函数的对象)

编辑:反思真的不一定要贵。 JVM在这方面有了很大的改进。 只需确保编译一次调用并重用它。

Edit2:示例代码

 public class MapExample { public static interface Function { public R apply(A b); } public static  Function compilePropertyMapper(Class objectType, String property, Class propertyType) { try { final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1)); if(!propertyType.isAssignableFrom(m.getReturnType())) throw new IllegalArgumentException( "Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName() ); return new Function() { @SuppressWarnings("unchecked") public R apply(A b) { try { return (R)m.invoke(b); } catch (Exception e) { throw new RuntimeException(e); } }; }; } catch (Exception e) { throw new RuntimeException(e); } } public static  List map(final List list, final Function mapper) { return new AbstractList() { @Override public T2 get(int index) { return mapper.apply(list.get(index)); } @Override public int size() { return list.size(); } }; } @SuppressWarnings("unchecked") public static  List mapToProperty(List list, String property, Class propertyType) { if(list == null) return null; else if(list.isEmpty()) return Collections.emptyList(); return map(list,compilePropertyMapper((Class)list.get(0).getClass(), property, propertyType)); } } 

你可以使用包装器:

 public class IdList impements List { private List underlying; pubic IdList(List underying) { this.underlying = underying; } public Long get(int index) { return underlying.get(index).getId() } // other List methods } 

虽然这是更繁琐的工作,但它可以提高性能。

您也可以使用reflection来实现您和我的解决方案,但这对于执行非常糟糕。

我不担心Java中没有简短易用的通用解决方案。 在Groovy中,你只需使用collect(),但我相信这也涉及到reflection。

这取决于你对List以及List

例如,您可以从创建自己的List实现(包含List iterator()获得足够的function,使用遍历ViewValues的迭代器实现实现iterator() ,返回id。