2017-06-02 121 views
7

我的列表包含像[1,3,5][2,6,4]等所有相同大小的集合。 我试过这样做,但它似乎没有工作。如何使用java8流对TreeSet列表进行排序

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,TreeSet's。但是是否有某种班轮?

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

这工作,但会这样被简化?

+0

可以肯定 - 你想重新排列元素,所以每个集合都有三个元素,集合本身是按升序排列的? – Mureinik

+0

确保有一个班轮,如果你从左边开始足够远,并将边距设置得足够远。一旦书面,没有人能够阅读它。 –

+0

是的,这是我想要的最终结果@Mureinik – LexByte

回答

10

@尤金的答案是甜的,因为番石榴是甜的。但是,如果你碰巧没有番石榴在类路径中,还有一种方法:

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

首先,我flatmapping所有集合到一个数据流,然后我排序的所有元素,最后,我将整个排序流收集到一组列表中。对于这一点,我调用使用自定义集电极一个辅助方法:

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本地类作为可变结构由集电极使用。在Acc类中,我使用的List将包含LinkedHashSet实例,该实例将保存流的元素。

Acc类保留已经收集的所有元素的数量。在add方法中,我计算列表的索引并增加此计数,并且如果在该列表的该位置中没有设置,则向其追加新的空LinkedHashSet。然后,我将元素添加到集合中。

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

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

最后,我使用Collector.of创建基于Acc类的方法的收集器。最后一个参数是一个修整器功能,它只返回Acc实例的列表。

+5

这两个都深思熟虑,很好地解释。 –

+1

喜欢在方法*中定义的*单目的类。 Minor nitpick:它不编译,因为'count ++/size'是一个'long'。 'count'应该是'int',或者如果保存为'long',那么'index'应该用'Math.toIntExact(count ++/size)'来计算(尽管我不确定它对于支持我们需要*排序的这样的大列表*)。 –

+0

嗨,@Hugues谢谢你指出。 'int count'应该这样做。 –

3

如果在classpath guava这是一件轻而易举的事:

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

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

添加另一个答案,因为这会比评论更大。这是真正被接受的答案,但是使用“智能”组合器,无需再一次流。

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); 
} 
相关问题