Java 8在分组时未能保持顺序

43

我正在使用Java 8对数据进行分组。但是得到的结果不是按照原来的顺序排列的。

Map<GroupingKey, List<Object>> groupedResult = null;

        if (!CollectionUtils.isEmpty(groupByColumns)) {

            Map<String, Object> mapArr[] = new LinkedHashMap[mapList.size()];

            if (!CollectionUtils.isEmpty(mapList)) {
                int count = 0;
                for (LinkedHashMap<String, Object> map : mapList) {
                    mapArr[count++] = map;
                }
            }
            Stream<Map<String, Object>> people = Stream.of(mapArr);
            groupedResult = people
                    .collect(Collectors.groupingBy(p -> new GroupingKey(p, groupByColumns), Collectors.mapping((Map<String, Object> p) -> p, toList())));


public static class GroupingKey 

        public GroupingKey(Map<String, Object> map, List<String> cols) {

            keys = new ArrayList<>();

            for (String col : cols) {
                keys.add(map.get(col));
            }
        }

        // Add appropriate isEqual() ... you IDE should generate this
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final GroupingKey other = (GroupingKey) obj;
            if (!Objects.equals(this.keys, other.keys)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 37 * hash + Objects.hashCode(this.keys);
            return hash;
        }

        @Override
        public String toString() {
            return keys + "";
        }

        public ArrayList<Object> getKeys() {
            return keys;
        }

        public void setKeys(ArrayList<Object> keys) {
            this.keys = keys;
        }

    }

我在使用我的类groupingKey,通过它我可以从用户界面动态传递。如何以排序形式获取这些groupByColumns?

2个回答

73

不维护顺序是存储结果的 Map 的一个属性。如果您需要特定的 Map 行为,您需要请求特定的 Map 实现。例如,LinkedHashMap 维护插入顺序:

groupedResult = people.collect(Collectors.groupingBy(
    p -> new GroupingKey(p, groupByColumns),
    LinkedHashMap::new,
    Collectors.mapping((Map<String, Object> p) -> p, toList())));
顺便提一下,在创建流之前将mapList的内容复制到数组中是没有必要的。您可以直接调用mapList.stream()来获取一个合适的流。
另外,Collectors.mapping((Map p) -> p, toList())已经过时。p->p是一个identity mapping操作,因此没有理由使用mapping。
groupedResult = mapList.stream().collect(Collectors.groupingBy(
    p -> new GroupingKey(p, groupByColumns), LinkedHashMap::new, toList()));
但是即使是GroupingKey也已经过时了。它基本上包装了一个值的List,所以你一开始就可以使用一个List作为键。 List适当地实现了hashCode和equals(但您不能在之后修改这些键List)。
Map<List<Object>, List<Object>> groupedResult=
  mapList.stream().collect(Collectors.groupingBy(
    p -> groupByColumns.stream().map(p::get).collect(toList()),
    LinkedHashMap::new, toList()));

谢谢Holger,您使我的任务变得更简单了。但我仍然有一个问题,如果我想在这个groupedColumns中应用排序,那该怎么办? - Shreya Shah
1
方法 toList() 未定义,是指 Collections.toList 吗? - gene b.
2
@geneb。Collectors.toList()。您可以使用import static java.util.stream.Collectors.*;来避免在组合收集器时重复使用Collectors. - Holger

15

基于 @Holger 的出色答案,我发布此帖以帮助那些想要在分组后保持顺序并更改映射的人。

让我们简化一下,假设我们有一个人员列表(int age,String name,String address...等),我们想根据年龄将姓名分组,同时保持年龄顺序:

final LinkedHashMap<Integer, List<String> map = myList
                .stream()
                .sorted(Comparator.comparing(p -> p.getAge())) //sort list by ages
                .collect(Collectors.groupingBy(p -> p.getAge()),
                        LinkedHashMap::new, //keeps the order
                        Collectors.mapping(p -> p.getName(), //map name
                                Collectors.toList())));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接