简体   繁体   English

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

[英]How do I sort a List of TreeSets with java8 streams

My list contains sets like [1,3,5][2,6,4] etc, all of the same size. 我的列表包含[1,3,5][2,6,4]等集合,大小相同。 I tried doing this but it doesn't seem to work. 我试过这样做,但似乎没有用。

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

    }

The end result I want is [1,2,3][4,5,6] . 我想要的最终结果是[1,2,3][4,5,6]

I could try to add all the elements in an ArrayList and sort that out then make a new List of TreeSet 's. 我可以尝试在ArrayList添加所有元素并对其进行排序,然后创建一个新的TreeSet List But is there is some kind of one liner? 但是有一种衬垫吗?

UPDATE: 更新:

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());

This works but could this be simplified? 这有效但可以简化吗?

@Eugene's answer is sweet, because Guava is sweet. @ Eugene的答案很甜蜜,因为番石榴很甜。 But if you happen to not have Guava in your classpath, here's another way: 但是,如果您的类路径中没有Guava,这是另一种方式:

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

First, I'm flatmapping all the sets into one stream, then I'm sorting all the elements and finally, I'm collecting the whole sorted stream to a list of sets. 首先,我将所有集合平面化为一个流,然后我将所有元素排序,最后,我将整个排序流收集到集合列表中。 For this, I'm invoking a helper method that uses a custom collector: 为此,我正在调用一个使用自定义收集器的辅助方法:

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);
}

The method receives the size of each partition and uses the Acc local class as the mutable structure to be used by the collector. 该方法接收每个分区的大小,并使用Acc本地类作为收集器使用的可变结构。 Inside the Acc class, I'm using a List that will contain LinkedHashSet instances, which will hold the elements of the stream. Acc类中,我使用的List将包含LinkedHashSet实例,它将保存流的元素。

The Acc class keeps the count of all the elements that have been already collected. Acc类保留已经收集的所有元素的计数。 In the add method, I calculate the index of the list and increment this count, and if there was no set in that position of the list, I append a new empty LinkedHashSet to it. add方法中,我计算列表的索引并递增此计数,如果列表的该位置没有设置,我向它添加一个新的空LinkedHashSet Then, I add the element to the set. 然后,我将元素添加到集合中。

As I'm calling sorted() on the stream to sort its elements before collecting, I need to use data structures that preserve insertion order. 由于我在流上调用sorted()以在收集之前对其元素进行排序,因此我需要使用保留插入顺序的数据结构。 This is why I'm using ArrayList for the outer list and LinkedHashSet for the inner sets. 这就是为什么我使用ArrayList作为外部列表,使用LinkedHashSet作为内部集合。

The merge method is to be used by parallel streams, to merge two previously accumulated Acc instances. merge方法将由并行流使用,以合并两个先前累积的Acc实例。 I'm just adding all the elements of the received Acc instance to this Acc instance, by delegating to the add method. 我只是通过委托add方法将收到的Acc实例的所有元素添加到此Acc实例中。

Finally, I'm using Collector.of to create a collector based on the methods of the Acc class. 最后,我使用Collector.of基于Acc类的方法创建一个收集器。 The last argument is a finisher function, which just returns the Acc instance's list. 最后一个参数是一个修整器函数, Acc返回Acc实例的列表。

If you have guava on the classpath this is a breeze: 如果你在类路径上有guava ,这是一件轻而易举的事:

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

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

Adding another answer since this would be bigger than a comment. 添加另一个答案,因为这将比评论更大。 It's really what the accepted answer has done, but with a "smarter" combiner that does not have to stream all the time again. 这确实是接受的答案所做的,但是使用“更智能”的组合器不需要一直流动。

 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM