Java 8:如何使用lambda将列表转换为列表

我正在尝试将列表拆分为列表,其中每个列表的最大大小为4。

我想知道如何使用lambdas做到这一点。

目前我正在这样做的方式如下:

List<List> listOfList = new ArrayList(); final int MAX_ROW_LENGTH = 4; int startIndex =0; while(startIndex <= listToSplit.size() ) { int endIndex = ( ( startIndex+MAX_ROW_LENGTH ) < listToSplit.size() ) ? startIndex+MAX_ROW_LENGTH : listToSplit.size(); listOfList.add(new ArrayList(listToSplit.subList(startIndex, endIndex))); startIndex = startIndex+MAX_ROW_LENGTH; } 

UPDATE

似乎没有一种简单的方法可以使用lambdas来拆分列表。 虽然所有答案都非常受欢迎,但它们也是lambdas不简化事物的一个很好的例子。

试试这种方法:

 static  List> listSplitter(List incoming, int size) { // add validation if needed return incoming.stream() .collect(Collector.of( ArrayList::new, (accumulator, item) -> { if(accumulator.isEmpty()) { accumulator.add(new ArrayList<>(singletonList(item))); } else { List last = accumulator.get(accumulator.size() - 1); if(last.size() == size) { accumulator.add(new ArrayList<>(singletonList(item))); } else { last.add(item); } } }, (li1, li2) -> { li1.addAll(li2); return li1; } )); } System.out.println( listSplitter( Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), 4 ) ); 

另请注意,此代码可以进行优化,而不是:

 new ArrayList<>(Collections.singletonList(item)) 

使用这一个:

 List> newList = new ArrayList<>(size); newList.add(item); return newList; 

如果你真的需要一个lambda,它就可以这样做。 否则以前的答案会更好。

  List> lists = new ArrayList<>(); AtomicInteger counter = new AtomicInteger(); final int MAX_ROW_LENGTH = 4; listToSplit.forEach(pO -> { if(counter.getAndIncrement() % MAX_ROW_LENGTH == 0) { lists.add(new ArrayList<>()); } lists.get(lists.size()-1).add(pO); }); 

也许你可以使用类似的东西

  BiFunction splitter= (list2, count)->{ //temporary list of lists List listOfLists=new ArrayList<>(); //helper implicit recursive function BiConsumer splitterHelper = (offset, func) -> { if(list2.size()> offset+count){ listOfLists.add(list2.subList(offset,offset+count)); //implicit self call func.accept(offset+count,func); } else if(list2.size()>offset){ listOfLists.add(list2.subList(offset,list2.size())); //implicit self call func.accept(offset+count,func); } }; //pass self reference splitterHelper.accept(0,splitterHelper); return listOfLists; }; 

用法示例

 List list=new ArrayList(){{ add(1); add(2); add(3); add(4); add(5); add(6); add(7); add(8); add(8); }}; //calling splitter function List listOfLists = splitter.apply(list, 3 /*max sublist size*/); System.out.println(listOfLists); 

结果我们有了

 [[1, 2, 3], [4, 5, 6], [7, 8, 8]] 

要求有点奇怪,但你可以这样做:

 final int[] counter = new int[] {0}; List> listOfLists = in.stream() .collect(Collectors.groupingBy( x -> counter[0]++ / MAX_ROW_LENGTH )) .entrySet().stream() .sorted(Map.Entry.comparingByKey()) .map(Map.Entry::getValue) .collect(Collectors.toList()); 

您可以使用groupingBy的变体来简化这一点,该变量采用mapSupplier lambda并提供SortedMap 。 这应该返回一个按顺序迭代的EntrySet 。 我把它留作练习。

我们在这里做的是:

  • 使用计数器组将列表项收集到Map 。 计数器保存在单元素数组中,因为lambda只能使用局部变量(如果它们是final
  • 将映射条目作为流获取,并按Integer键排序。
  • 使用Stream::map()Map.Entry的流转换为Object值流。
  • 将其收集到列表中。

这不会受益于任何“免费”并行化。 它在中间Map有内存开销。 这不是特别容易阅读。


但是,我不会这样做,只是为了使用lambda。 我会做的事情如下:

 for(int i=0; i 

(你有一个防御性的复制new ArrayList<>(listToSplit.subList(...)) 。我没有复制它,因为它并不总是必要的 - 例如,如果输入列表是不可修改的,并且输出列表不打算如果您决定在您的情况下需要它,请将其重新安装。)

任何内存列表都会非常快。 你不太可能想要并行化。


或者,您可以编写自己的(不可修改的) List实现,它是基础List的视图:

 public class PartitionedList extends AbstractList> { private final List source; private final int sublistSize; public PartitionedList(T source, int sublistSize) { this.source = source; this.sublistSize = sublistSize; } @Override public int size() { return source.size() / sublistSize; } @Override public List get(int index) { int sourceIndex = index * sublistSize return source.subList(sourceIndex, Math.min(sourceIndex + sublistSize, source.size()); } } 

再次,你是否想要在这里制作防御性副本取决于你。

这将与基础列表具有相同的大O访问时间。

当然,下面就足够了

 final List> listOfList = new ArrayList<>( listToSplit.stream() .collect(Collectors.groupingBy(el -> listToSplit.indexOf(el) / MAX_ROW_LENGTH)) .values() ); 

流式传输,使用分组进行收集:这会给出一个对象的Map – > List,拉出地图的值并直接传递给任何构造函数(map.values()给出Collection而不是List)。