【问题标题】:Dynamic grouping by specific attributes with sorting通过特定属性进行动态分组和排序
【发布时间】:2023-04-10 19:59:01
【问题描述】:

除了对分组属性进行排序之外,我的问题已通过以下答案解决。

Dynamic grouping by specific attributes with Collection.stream

如何根据升序或降序的属性从按排序顺序设置的值中检索列表。一个组属性可以升序,而另一个可以降序。

我创建了一个 GroupClass 作为参数而不是字符串数组。

    public class MyClass {
    public String title;
    public String type;
    public String module;

    public MyClass(String title, String type, String module) {
        this.type = type;
        this.title = title;
        this.module = module;
    }

    @Override
    public String toString() {
        return "MyClass [title=" + title + ", type=" + type + ", module=" + module + "]";
    }

    private static Map<List<String>, List<MyClass>> groupListBy(List<MyClass> data, List<GroupClass> groupFieldList) {
        final MethodHandles.Lookup lookup = MethodHandles.lookup();
        List<MethodHandle> handles = groupFieldList.stream().map(groupField -> {
            try {
                return lookup.findGetter(MyClass.class, groupField.getFieldName(), String.class);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());

        return data.stream().collect(Collectors.groupingBy(d -> handles.stream().map(handle -> {
            try {
                return (String) handle.invokeExact(d);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList())));
    }

    public static void main(String[] args) {
        List<MyClass> data = new ArrayList<>();
        data.add(new MyClass("Title1", "TYPEA", "MB"));
        data.add(new MyClass("Title2", "TYPEA", "MB"));
        data.add(new MyClass("Title3", "TYPEA", "MC"));
        data.add(new MyClass("Title4", "TYPEB", "MA"));

        List<GroupClass> groupFieldList = new ArrayList<>();
        groupFieldList.add(new GroupClass("type","asc"));
        groupFieldList.add(new GroupClass("module","asc"));

        System.out.println(groupListBy(data, groupFieldList));
    }
}

    public class GroupClass {

    String name;
    String order;

    public GroupClass(String fieldName, String fieldOrder) {
        super();
        this.name = fieldName;
        this.order = fieldOrder;
    }

    public String getFieldName() {
        return name;
    }
    public void setFieldName(String fieldName) {
        this.name = fieldName;
    }
    public String getFieldOrder() {
        return order;
    }
    public void setFieldOrder(String fieldOrder) {
        this.order = fieldOrder;
    }
}

输出:

{
[TYPEA, MC]=[MyClass [title=Title3, type=TYPEA, module=MC]], 
[TYPEA, MB]=[MyClass [title=Title1, type=TYPEA, module=MB], MyClass [title=Title2, type=TYPEA, module=MB]], 
[TYPEB, MA]=[MyClass [title=Title4, type=TYPEB, module=MA]]
}

我想要达到的输出:

{
[TYPEB, MA]=[MyClass [title=Title4, type=TYPEB, module=MA]],
[TYPEA, MB]=[MyClass [title=Title1, type=TYPEA, module=MB], MyClass [title=Title2, type=TYPEA, module=MB]],
[TYPEA, MC]=[MyClass [title=Title3, type=TYPEA, module=MC]] 
}

以上输出按“type”列降序排序,“module”列升序排序。

【问题讨论】:

  • 链接问答的代码产生一个Map&lt;List&lt;String&gt;, List&lt;MyClass&gt;&gt;你想按什么标准排序?
  • 如果您期待排序,请尝试流式传输获得的Map 条目并根据Comparator(作为方法参数)对它们进行排序,同时将它们收集回@ 987654329@.
  • 您可以创建两个比较器,一个用于title,另一个用于module,然后通过使用这两个比较器来使用chained comparator
  • 在您的示例代码中,您使用"asc" 初始化两个GroupClass 对象,这与您希望将"type" 列按降序排列的描述相矛盾。当你解决这个问题时,my solutions 都会提供所需的结果。

标签: java sorting java-8 group-by comparator


【解决方案1】:

有两种方法可以做到这一点

  1. 使用 java-8
   Map<Object, List<MyClass>> sortedModuleList=
                data.stream().collect(Collectors.groupingBy(k -> k.module));
   System.out.println(sortedModuleList);

下面的代码sn-p:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class GroupByModule {

    public static void main(String[] args) {
        MyClass p1 = new MyClass("Title1","TYPEA","MB");
        MyClass p2 = new MyClass("Title2","TYPEA","MB");
        MyClass p3 = new MyClass("Title3","TYPEA","MC");
        MyClass p4 = new MyClass("Title4","TYPEB","MA");

        List<MyClass> list = new ArrayList<MyClass>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);

        Map<Object, List<MyClass>> sortedModuleList =
                list.stream().collect(Collectors.groupingBy(k -> k.module));
        System.out.println(sortedModuleList);
        }
    }


class MyClass {
        public String title;
        public String type;
        public String module;

        public MyClass(String string, String string2, String string3) {
            // TODO Auto-generated constructor stub
               this.type = type;
               this.title = title;
               this.module = module;
        }

        @Override
        public String toString() {
            return "MyClass [title=" + title + ", type=" + type + ", module=" + module + "]";
        }
}

输出:

{ 
   MA=[MyClass [title=Title4, type=TYPEB, module=MA]], 
   MB=[MyClass [title=Title1, type=TYPEA, module=MB], MyClass [title=Title2, type=TYPEA, module=MB]], 
   MC=[MyClass [title=Title3, type=TYPEA, module=MC]] 
}

  1. 使用链式比较器

想象一下,如果我们没有 java8 stream 功能,那么我们需要这样做。这是一种有点旧的编码方式:

  1. 让我们创建一个 bean MyClass

MyClass.java

public class MyClass {

    public String title;
    public String type;
    public String module;

    public MyClass(String title, String type, String module) {
        this.type = type;
        this.title = title;
        this.module = module;
    }
    @Override
    public String toString() {
        return "MyClass [title=" + title + ", type=" + type + ", module=" + module + "]";
    }
 //getter and setter
}
  1. 找出需要排序的字段数,在本例中,首先是Type,然后是Module。我们需要创建两个比较器,一个用于Type,一个用于Module

TypeComparator.java

import java.util.Comparator;
public class TypeComparator implements Comparator<MyClass>{

    @Override
    public int compare(MyClass obj1, MyClass obj2) {

         return obj1.getType().compareTo(obj2.getType());
    }
}

Modulecomparator.java

import java.util.Comparator;
public class Modulecomparator implements Comparator<MyClass>{

    @Override
    public int compare(MyClass obj1, MyClass obj2) {

         return obj1.getModule().compareTo(obj2.getModule());
    }
}
  1. 现在我们需要编写一个ChainedComparator,它充当TypeComparatorModuleComparator 之间的桥梁。

MyClassChainedComparator.java

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class MyClassChainedComparator implements Comparator<MyClass> {

     private List<Comparator<MyClass>> listComparators;


     public MyClassChainedComparator(Comparator<MyClass>... comparators) {
         this.listComparators = Arrays.asList(comparators);
     }

     @Override
     public int compare(MyClass myclass1, MyClass myclass2) {
         for (Comparator<MyClass> comparator : listComparators) {
             int result = comparator.compare(myclass1, myclass2);
             if (result != 0) {
                 return result;
             }
         }
         return 0;
     }
}
  1. MyClassChainedComparator 的帮助下,通过在TypeComparatorModuleComparator 之间进行链接来执行sort,如下所示:

MyClassResult.java

public class MyClassResult {
    public static void main(String[] args)
    {

        ArrayList<MyClass> list = new ArrayList<>();
        list.add( new MyClass("Title1", "TYPEA", "MB") );
        list.add( new MyClass("Title2", "TYPEA", "MB") );
        list.add( new MyClass("Title3", "TYPEA", "MC") );
        list.add(new MyClass("Title4", "TYPEB", "MA") );

        Collections.sort(list,new MyClassChainedComparator(new Modulecomparator(),new TypeComparator()));

        for(MyClass myc:list){
            System.out.println(myc);
        }
}
}

输出:

MyClass [title=Title4, type=TYPEB, module=MA]
MyClass [title=Title1, type=TYPEA, module=MB]
MyClass [title=Title2, type=TYPEA, module=MB]
MyClass [title=Title3, type=TYPEA, module=MC]

【讨论】:

    【解决方案2】:

    确定您的组的方式与对它们进行排序的任务无关。在您的流程结束时,您将拥有一个 Map&lt;List&lt;String&gt;, List&lt;MyClass&gt;&gt; 和包含所有必要信息的 List&lt;String&gt; 键。

    所以任务归结为对List&lt;String&gt; 值进行排序。这可以通过组合比较各个元素的比较器来完成。

    Comparator<List<String>> comp = IntStream.range(0, groupFieldList.size())
        .mapToObj(i -> {
            Comparator<List<String>> c = Comparator.comparing(l -> l.get(i));
             // you should better use a boolean or enum for the direction
            if(!groupFieldList.get(i).getFieldOrder().equals("asc"))
                c = c.reversed();
            return c;
        })
        .reduce(Comparator::thenComparing).orElse((a,b) -> 0);
    

    那么,有两种选择。第一种是使用比较器收集到一个排序的地图中:

    return data.stream().collect(Collectors.groupingBy(
        d -> handles.stream().map(handle -> {
            try {
                return (String) handle.invokeExact(d);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList()),
        () -> new TreeMap<>(comp),
        Collectors.toList()));
    

    第二个是和之前一样collect into a map,然后是另一个流操作,对元素进行排序,并collection成一个保留插入顺序的map:

    return data.stream().collect(Collectors.groupingBy(d -> handles.stream().map(handle -> {
            try {
                return (String) handle.invokeExact(d);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList())))
        .entrySet().stream()
        .sorted(Map.Entry.comparingByKey(comp))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
            (a,b)->{throw new AssertionError();}, LinkedHashMap::new));
    

    由于我们是按键排序而不是计算的组值,因此我们可以将第一个分组替换为一个到包含键和(尚未聚合的)值的条目的普通映射,并在第二个操作中收集组值:

    return data.stream()
        .map(d -> new AbstractMap.SimpleEntry<>(handles.stream().map(handle -> {
            try {
                return (String) handle.invokeExact(d);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList()), d))
        .sorted(Map.Entry.comparingByKey(comp))
        .collect(Collectors.groupingBy(Map.Entry::getKey,
            LinkedHashMap::new,
            Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-21
      • 2016-08-19
      • 1970-01-01
      • 1970-01-01
      • 2018-02-27
      • 1970-01-01
      相关资源
      最近更新 更多