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

时间:2009-04-10 10:22:39

标签: java list properties

我将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<Long> toIdsList(List<ViewValue> viewValues) {

   List<Long> ids = new ArrayList<Long>();

   for (ViewValue viewValue : viewValues) {
      ids.add(viewValue.getId());
   }

   return ids;

}

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

8 个答案:

答案 0 :(得分:33)

你可以使用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"));

答案 1 :(得分:28)

使用谷歌收藏。例如:

    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);

<强>更新

在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());

或者只是:

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

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

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

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

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

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

但是你会看到只有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);

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

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

答案 2 :(得分:28)

我们可以使用java 8

在一行代码中完成
List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList());

了解详情: Java 8 - Streams

答案 3 :(得分:27)

编辑:这个答案是基于这样的想法,即您需要为代码中的其他实体和其他属性执行类似的操作。如果需要将ViewValues列表转换为ID列表,请坚持使用原始代码。但是,如果您想要一个更可重用的解决方案,请继续阅读......

我会为投影声明一个界面,例如

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

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

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;
}

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

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

并按照以下方式应用它:

List<Long> ids = convertAll(values, ID_PROJECTION);

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

坦率地说,所有这些对于lambda表达式来说会更好,但不要介意......

答案 4 :(得分:4)

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

<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType)

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

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

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

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

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));
    }
}

答案 5 :(得分:3)

你可以使用包装器:

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
}

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

你也可以使用反射来实现你和我的解决方案,但这对于穿孔来说非常糟糕。

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

答案 6 :(得分:2)

这取决于您对List<Long>以及List<ViewValue>

所做的事情

例如,您可以通过创建自己的包含List<ViewValue>的List实现来获得足够的功能,使用遍历ViewValues的迭代器实现来实现iterator(),并返回id。

答案 7 :(得分:0)

您可以通过以下对象列表的属性(例如,id为键,某些属性为值)填充地图

Map<String, Integer> mapCount = list.stream().collect(Collectors.toMap(Object::get_id, Object::proprty));