如何使用 java8 流对 TreeSet 列表进行排序

How do I sort a List of TreeSets with java8 streams

我的列表包含 [1,3,5][2,6,4] 等集合,大小都相同。 我尝试这样做,但它似乎不起作用。

List<TreeSet<T>> block;
    for(TreeSet<T> t : block){
        block.stream().sorted((n,m)->n.compareTo(m)).collect(Collectors.toSet());

    }

我想要的最终结果是[1,2,3][4,5,6]

我可以尝试添加 ArrayList 中的所有元素并对其进行排序,然后制作一个新的 List of TreeSet。但是有没有一种班轮?

更新:

List<T> list=new ArrayList<T>();
    for(TreeSet<T> t : block){

        for(T t1 : t)
        {
            list.add(t1);   

        }
    }

    list=list.stream().sorted((n,m)->n.compareTo(m)).collect(Collectors.toList());

这可行,但可以简化吗?

如果类路径上有 guava,这很容易:

        block
            .stream()
            .flatMap(Set::stream)
            .collect(Collectors.toCollection(TreeSet::new));

    Iterable<List<Integer>> result = Iterables.partition(sorted, 3);

@Eugene 的回答很甜,因为番石榴很甜。但是如果你的 class 路径中没有 Guava,还有另一种方法:

List<Set<Integer>> list = block.stream()
    .flatMap(Set::stream)
    .sorted()
    .collect(partitioning(3));

首先,我将所有集合平面映射到一个流中,然后对所有元素进行排序,最后,我将整个排序后的流收集到一个集合列表中。为此,我正在调用一个使用自定义收集器的辅助方法:

private static <T> Collector<T, ?, List<Set<T>>> partitioning(int size) {
    class Acc {
        int count = 0;
        List<Set<T>> list = new ArrayList<>();

        void add(T elem) {
            int index = count++ / size;
            if (index == list.size()) list.add(new LinkedHashSet<>());
            list.get(index).add(elem);
        }

        Acc merge(Acc another) {
            another.list.stream().flatMap(Set::stream).forEach(this::add);
            return this;
        }
    }
    return Collector.of(Acc::new, Acc::add, Acc::merge, acc -> acc.list);
}

该方法接收每个分区的大小,并使用Acc本地class作为收集器使用的可变结构。在 Acc class 中,我使用的 List 将包含 LinkedHashSet 个实例,它将保存流的元素。

Acc class 保留所有已收集元素的计数。在 add 方法中,我计算列表的索引并递增此计数,如果列表的那个位置没有集合,我向其附加一个新的空 LinkedHashSet 。然后,我将元素添加到集合中。

因为我在流上调用 sorted() 以在收集之前对其元素进行排序,所以我需要使用保留插入顺序的数据结构。这就是为什么我对外部列表使用 ArrayList 而对内部集合使用 LinkedHashSet 的原因。

merge 方法将由并行流使用,合并两个先前累积的 Acc 实例。我只是通过委托给 add 方法,将收到的 Acc 实例的所有元素添加到此 Acc 实例。

最后,我正在使用 Collector.of 创建一个基于 Acc class 方法的收集器。最后一个参数是一个 finisher 函数,它只是 returns Acc 个实例的列表。

添加另一个答案,因为这会比评论大。这确实是公认的答案所做的,但是使用 "smarter" 组合器不必再次流式传输。

 private static <T> Collector<T, ?, List<Set<T>>> partitioning(int size) {
    class Acc {
        int count = 0;

        List<List<T>> list = new ArrayList<>();

        void add(T elem) {
            int index = count++ / size;
            if (index == list.size()) {
                list.add(new ArrayList<>());
            }
            list.get(index).add(elem);
        }

        Acc merge(Acc right) {

            List<T> lastLeftList = list.get(list.size() - 1);
            List<T> firstRightList = right.list.get(0);
            int lastLeftSize = lastLeftList.size();
            int firstRightSize = firstRightList.size();

            // they have both the same size, simply addAll will work
            if (lastLeftSize + firstRightSize == 2 * size) {
                System.out.println("Perfect!");
                list.addAll(right.list);
                return this;
            }

            // last and first from each chunk are merged "perfectly"
            if (lastLeftSize + firstRightSize == size) {
                System.out.println("Almost perfect");
                int x = 0;
                while (x < firstRightSize) {
                    lastLeftList.add(firstRightList.remove(x));
                    --firstRightSize;
                }
                right.list.remove(0);
                list.addAll(right.list);
                return this;
            }

            right.list.stream().flatMap(List::stream).forEach(this::add);
            return this;
        }

        public List<Set<T>> finisher() {
            return list.stream().map(LinkedHashSet::new).collect(Collectors.toList());
        }

    }
    return Collector.of(Acc::new, Acc::add, Acc::merge, Acc::finisher);
}