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
然后你可以像这样定义简单的投影:
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。