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)。