Java - 将对象列表映射到具有其属性属性值的列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/737244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java - map a list of objects to a list with values of their property attributes
提问by mgamer
I have the ViewValue class defined as follows:
我的 ViewValue 类定义如下:
class ViewValue {
private Long id;
private Integer value;
private String description;
private View view;
private Double defaultFeeRate;
// getters and setters for all properties
}
Somewhere in my code i need to convert a list of ViewValue instances to a list containing values of id fields from corresponding ViewValue.
在我的代码中的某个地方,我需要将 ViewValue 实例列表转换为包含来自相应 ViewValue 的 id 字段值的列表。
I do it using foreach loop:
我使用 foreach 循环来做到这一点:
List<Long> toIdsList(List<ViewValue> viewValues) {
List<Long> ids = new ArrayList<Long>();
for (ViewValue viewValue : viewValues) {
ids.add(viewValue.getId());
}
return ids;
}
}
Is there a better approach to this problem?
有没有更好的方法来解决这个问题?
采纳答案by Jon Skeet
EDIT: This answer is based on the idea that you'll need to do similar things for different entities and different properties elsewhere in your code. If you onlyneed to convert the list of ViewValues to a list of Longs by ID, then stick with your original code. If you want a more reusable solution, however, read on...
编辑:此答案基于您需要为代码中其他地方的不同实体和不同属性执行类似操作的想法。如果您只需要按 ID 将 ViewValues 列表转换为 Longs 列表,那么请坚持使用您的原始代码。但是,如果您想要一个更可重用的解决方案,请继续阅读...
I would declare an interface for the projection, e.g.
我会为投影声明一个接口,例如
public interface Function<Arg,Result>
{
public Result apply(Arg arg);
}
Then you can write a single generic conversion method:
然后你可以编写一个通用的转换方法:
public <Source, Result> List<Result> convertAll(List<Source> source,
Function<Source, Result> projection)
{
ArrayList<Result> results = new ArrayList<Result>();
for (Source element : source)
{
results.add(projection.apply(element));
}
return results;
}
Then you can define simple projections like this:
然后你可以像这样定义简单的投影:
private static final Function<ViewValue, Long> ID_PROJECTION =
new Function<ViewValue, Long>()
{
public Long apply(ViewValue x)
{
return x.getId();
}
};
And apply it just like this:
并像这样应用它:
List<Long> ids = convertAll(values, ID_PROJECTION);
(Obviously using K&R bracing and longer lines makes the projection declaration a bit shorter :)
(显然,使用 K&R 支撑和更长的线会使投影声明更短:)
Frankly all of this would be a lot nicer with lambda expressions, but never mind...
坦率地说,所有这些对于 lambda 表达式都会好得多,但没关系......
回答by Stephen Denne
That depends on what you then do with the List<Long>
, and the List<ViewValue>
这取决于您随后如何处理List<Long>
, 以及List<ViewValue>
For example you might get sufficient functionality from creating your own List implementation that wraps a List<ViewValue>
, implementing iterator()
with an iterator implementation that iterates over the ViewValues, returning the id.
例如,您可以通过创建自己的包含 a 的 List 实现获得足够的功能List<ViewValue>
,iterator()
使用迭代器实现来实现迭代 ViewValues,返回 id。
回答by Michael Borgwardt
You could ude a wrapper:
你可以使用一个包装器:
public class IdList impements List<Long>
{
private List<ViewValue> underlying;
pubic IdList(List<ViewValue> underying)
{
this.underlying = underying;
}
public Long get(int index)
{
return underlying.get(index).getId()
}
// other List methods
}
though thats even more tedious work, it could improve performance.
虽然那是更乏味的工作,但它可以提高性能。
You could also implement your and my solution genericaly using reflection, but that woud be very bad for perforance.
您也可以使用反射来实现您和我的解决方案,但这对性能非常不利。
Theres no short and easy generic solution in Java, Im afraid. In Groovy, you would simply use collect(), but I believe that involves reflection as well.
恐怕在 Java 中没有简短易行的通用解决方案。在 Groovy 中,您只需使用 collect(),但我相信这也涉及反射。
回答by John Nilsson
I've implemented a small functional library for this usecase. One of the methods has this signature:
我为这个用例实现了一个小型函数库。其中一种方法具有以下签名:
<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType)
Which takes the string and uses reflection to create a call to the property then it returns a List backed by the objectList where get and iterator implemented using this property call.
它接受字符串并使用反射来创建对属性的调用,然后它返回一个由 objectList 支持的 List,其中使用此属性调用实现了 get 和迭代器。
The mapToProperty functions is implemented in terms of a general map function that takes a Function as a mapper though, just as another post described. Very usefull.
mapToProperty 函数是根据通用映射函数实现的,该函数将函数作为映射器,正如另一篇文章所述。非常有用。
I suggest you read up on basic functionl programming and in particular take a look at Functors (objects implementing a map function)
我建议你阅读基本的函数式编程,特别是看看 Functors(实现 map 函数的对象)
Edit: Reflection really doesn't have to be expensive. The JVM has improved a lot in this area. Just make sure to compile the invocation once and reuse it.
编辑:反射真的不必很贵。JVM 在这方面改进了很多。只需确保编译一次调用并重用它。
Edit2: Sample code
Edit2:示例代码
public class MapExample {
public static interface Function<A,R>
{
public R apply(A b);
}
public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> 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<A,R>()
{
@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 <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper)
{
return new AbstractList<T2>()
{
@Override
public T2 get(int index) {
return mapper.apply(list.get(index));
}
@Override
public int size() {
return list.size();
}
};
}
@SuppressWarnings("unchecked")
public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType)
{
if(list == null)
return null;
else if(list.isEmpty())
return Collections.emptyList();
return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType));
}
}
回答by Ulf Lindback
You could do it in a one-liner using Commons BeanUtils and Collections:
(why write your own code when others have done it for you?)
您可以使用 Commons BeanUtils 和 Collections 以单行方式完成:(
当其他人为您完成时,为什么要编写自己的代码?)
import org.apache.commons.beanutils.BeanToPropertyValueTransformer;
import org.apache.commons.collections.CollectionUtils;
...
List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues,
new BeanToPropertyValueTransformer("id"));
回答by Przemek Nowak
Use google collections. Example:
使用谷歌收藏。例子:
Function<ViewValue, Long> transform = new Function<ViewValue, Long>() {
@Override
public Long apply(ViewValue from) {
return from.getId();
}
};
List<ViewValue> list = Lists.newArrayList();
List<Long> idsList = Lists.transform(list, transform);
UPDATE:
更新:
On Java 8 you don't need Guava. You can:
在 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<ViewValue, Long> transform = ViewValue::getId;
List<ViewValue> source = new ArrayList<>();
List<Long> result = source.stream().map(transform).collect(Collectors.toList());
Or just:
要不就:
List<ViewValue> source= new ArrayList<>();
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList());
NEXT UPDATE (The last one after Javaslang to Vavr name change):
下一个更新(Javaslang 到 Vavr 名称更改后的最后一个):
Currently it's worth to mention about the solution with Javaslang library(http://www.javaslang.io/)Vavr library (http://www.vavr.io/). Let's assume that we have our list with genuine objects:
目前,它是值得一提的关于与解决方案Javaslang库(http://www.javaslang.io/)Vavr库(http://www.vavr.io/)。让我们假设我们有包含真实物品的列表:
List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2));
We could make transformation with List class from Javaslang library (on the long run the collect is not convenient):
我们可以使用 Javaslang 库中的 List 类进行转换(从长远来看,collect 不方便):
List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList();
But you will see the power with only the Javaslang lists:
但是您将看到只有 Javaslang 列表的强大功能:
io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3));
io.vavr.collection.List<Long> res = source.map(ViewValue::getId);
I encourage to take a look available collections and new types on that library (I like especially the Try type). You will find the documentation under the following address: http://www.javaslang.io/javaslang-docs/http://www.vavr.io/vavr-docs/.
我鼓励查看该库上的可用集合和新类型(我特别喜欢 Try 类型)。您将在以下地址下找到文档:http: //www.javaslang.io/javaslang-docs/http://www.vavr.io/vavr-docs/。
PS. Due to the Oracle and the "Java" word within the name they had to change the library name from javaslang to something else. They had decided to Vavr.
附注。由于 Oracle 和名称中的“Java”字样,他们不得不将库名称从 javaslang 更改为其他名称。他们已经决定去Vavr。
回答by Radhakrishna Sharma Gorenta
We can do it in a single line of code using java 8
我们可以使用 java 8 在一行代码中完成
List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList());
For more info : Java 8 - Streams
有关更多信息:Java 8 - 流
回答by Pravin
You can populate a map from the properties of a list of objects (say id as key and some property as value) as below
您可以从对象列表的属性(例如 id 作为键和某些属性作为值)填充映射,如下所示
Map<String, Integer> mapCount = list.stream().collect(Collectors.toMap(Object::get_id, Object::proprty));