Dynamic grouping by specific attributes with Collection.stream

后端 未结 2 2109
Happy的楠姐
Happy的楠姐 2020-12-15 11:57

I am trying to group a list of objects by mulitple attributes, by using Java 8 Collection-Stream.

This works pretty well:

public class MyClass
{
   p         


        
2条回答
  •  执笔经年
    2020-12-15 12:16

    The main problem with making that code more dynamic is that you don't know in advance how many elements there will be to group by. In such a case, it is best to group by the List of all the elements. This works because two lists are equal if all of their elements are equal and in the same order.

    In this case, instead of grouping by the type and then the module, we will group by the list consisting of each data type and module.

    private static Map, List> groupListBy(List data, String[] groupByFieldNames) {
        final MethodHandles.Lookup lookup = MethodHandles.lookup();
        List handles = 
            Arrays.stream(groupByFieldNames)
                  .map(field -> {
                      try {
                          return lookup.findGetter(MyClass.class, field, String.class);
                      } catch (Exception e) {
                          throw new RuntimeException(e);
                      }
                  }).collect(toList());
        return data.stream().collect(groupingBy(
                d -> handles.stream()
                            .map(handle -> {
                                try {
                                    return (String) handle.invokeExact(d);
                                } catch (Throwable e) {
                                    throw new RuntimeException(e);
                                }
                            }).collect(toList())
            ));
    }
    

    The first part of the code transforms the array of field names into a List of MethodHandle. For each field, a MethodHandle is retrieved for that field: this is done by obtaining a lookup from MethodHandles.lookup() and looking up a handle for the given field name with findGetter:

    Produces a method handle giving read access to a non-static field.

    The rest of the code creates the classifier to group by from. All the handles are invoked on the data instance to return the list of String value. This Stream is collected into a List to serve as classifier.

    Sample code:

    public static void main(String[] args) {
        List data = new ArrayList<>();
        data.add(new MyClass("1", "A", "B"));
        data.add(new MyClass("2", "A", "B"));
        data.add(new MyClass("3", "A", "C"));
        data.add(new MyClass("4", "B", "A"));
    
        System.out.println(groupListBy(data, new String[] { "type", "module" }));
    }
    

    Output:

    {[B, A]=[4], [A, B]=[1, 2], [A, C]=[3]}
    

    when MyClass.toString() is overriden to return the title only.

提交回复
热议问题