Java - map a list of objects to a list with values of their property attributes

后端 未结 8 1761
星月不相逢
星月不相逢 2020-12-01 01:28

I have the ViewValue class defined as follows:

class ViewValue {

private Long id;
private Integer value;
private String description;
private View view;
priv         


        
相关标签:
8条回答
  • 2020-12-01 01:48

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

    import org.apache.commons.beanutils.BeanToPropertyValueTransformer;
    import org.apache.commons.collections.CollectionUtils;
    
    ...
    
    List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues, 
                                           new BeanToPropertyValueTransformer("id"));
    
    0 讨论(0)
  • 2020-12-01 01:48

    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.

    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.

    I suggest you read up on basic functionl programming and in particular take a look at Functors (objects implementing a map function)

    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.

    Edit2: Sample code

    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));
        }
    }
    
    0 讨论(0)
  • 2020-12-01 01:53

    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:

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

    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:

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

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

    But you will see the power with only the Javaslang lists:

    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/.

    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.

    0 讨论(0)
  • 2020-12-01 01:55

    We can do it in a single line of code using java 8

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

    For more info : Java 8 - Streams

    0 讨论(0)
  • 2020-12-01 01:56

    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 only need 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...

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

    Frankly all of this would be a lot nicer with lambda expressions, but never mind...

    0 讨论(0)
  • 2020-12-01 02:06

    That depends on what you then do with the List<Long>, and the 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.

    0 讨论(0)
提交回复
热议问题