我正在尝试将列表拆分为列表,其中每个列表的最大大小为4.
我想知道如何使用lambdas。
目前我正在这样做的方式如下:
List<List<Object>> 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;
}
更新
似乎没有一种简单的方法可以使用lambdas来拆分列表。虽然所有的答案都非常受欢迎,但它们也是lambdas不简化事物的一个很好的例子。
答案 0 :(得分:5)
尝试这种方法:
static <T> List<List<T>> listSplitter(List<T> 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<T> 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<List<T>> newList = new ArrayList<>(size);
newList.add(item);
return newList;
答案 1 :(得分:3)
如果你真的需要一个lambda,可以这样做。否则以前的答案会更好。
List<List<Object>> 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);
});
答案 2 :(得分:2)
要求有点奇怪,但你可以这样做:
final int[] counter = new int[] {0};
List<List<Object>> 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<Integer,Object>
。计数器保存在单个元素数组中,因为lambda只能使用局部变量final
。Integer
键排序。Stream::map()
将Map.Entry<Integer,Object>
的流转换为Object
值的流。这不会受益于任何“免费”并行化。它在中间Map
中有内存开销。这不是特别容易阅读。
但是,我不会这样做,只是为了使用lambda。我会做类似的事情:
for(int i=0; i<in.size(); i += MAX_ROW_LENGTH) {
listOfList.add(
listToSplit.subList(i, Math.min(i + MAX_ROW_LENGTH, in.size());
}
(你的防御副本new ArrayList<>(listToSplit.subList(...))
。我没有重复它,因为它并不总是必要的 - 例如,如果输入列表是不可修改的,并且输出列表不是可修改的。如果你决定在你的情况下需要它,请回来。)
在任何内存列表中都会非常快。你不太可能想要并行化。
或者,您可以编写自己的(不可修改的)List
实现,这是对基础List<Object>
的一种看法:
public class PartitionedList<T> extends AbstractList<List<T>> {
private final List<T> 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<T> get(int index) {
int sourceIndex = index * sublistSize
return source.subList(sourceIndex,
Math.min(sourceIndex + sublistSize, source.size());
}
}
再次,你是否想在这里制作防御性副本取决于你。
这将与基础列表具有相同的大O访问时间。
答案 3 :(得分:2)
也许你可以使用类似的东西
BiFunction<List,Integer,List> splitter= (list2, count)->{
//temporary list of lists
List<List> listOfLists=new ArrayList<>();
//helper implicit recursive function
BiConsumer<Integer,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<Integer> list=new ArrayList<Integer>(){{
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]]
答案 4 :(得分:2)
当然,以下就足够了
final List<List<Object>> listOfList = new ArrayList<>(
listToSplit.stream()
.collect(Collectors.groupingBy(el -> listToSplit.indexOf(el) / MAX_ROW_LENGTH))
.values()
);
流式传输,使用分组进行收集:这会给出一个对象地图 - &gt;列表,拉取地图的值并直接传递到任何构造函数(map.values()给出Collection而不是List。)
答案 5 :(得分:1)
您可以使用:
ListUtils.partition(List list, int size)
或
List<List> partition(List list, int size)
两者都返回一个列表的连续子列表,每个子列表的大小相同(最终列表可能更小)。