scala 在java 8中将列表拆分为具有固定数量元素的多个列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28210775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 06:52:46  来源:igfitidea点击:

Split list into multiple lists with fixed number of elements in java 8

javascalajava-8

提问by vamosrafa

I want to something which is similar to the scala grouped function. Basically, pick 2 elements at a time and process them. Here is a reference for the same :

我想要一些类似于 scala 分组函数的东西。基本上,一次选择 2 个元素并处理它们。这是相同的参考:

Split list into multiple lists with fixed number of elements

将列表拆分为具有固定元素数量的多个列表

Lambdas do provide things like groupingBy and partitioningBy but none of them seem to do the same as the grouped function in Scala. Any pointers would be appreciated.

Lambda 确实提供了 groupingBy 和 partitioningBy 之类的东西,但它们似乎都没有像 Scala 中的 grouped 函数那样做。任何指针将不胜感激。

回答by haki

You can use Guavalibrary.

您可以使用番石榴库。

List<Integer> bigList = ... List<List<Integer>> smallerLists = Lists.partition(bigList, 10);

List<Integer> bigList = ... List<List<Integer>> smallerLists = Lists.partition(bigList, 10);

回答by Holger

It sounds like a problem that is better handled like a low-level Streamoperation just like the ops provided by the StreamAPI itself. A (relative) simple solution may look like:

这听起来像一个StreamStreamAPI 本身提供的操作一样的低级操作更好地处理的问题。(相对)简单的解决方案可能如下所示:

public static <T> Stream<List<T>> chunked(Stream<T> s, int chunkSize) {
    if(chunkSize<1) throw new IllegalArgumentException("chunkSize=="+chunkSize);
    if(chunkSize==1) return s.map(Collections::singletonList);
    Spliterator<T> src=s.spliterator();
    long size=src.estimateSize();
    if(size!=Long.MAX_VALUE) size=(size+chunkSize-1)/chunkSize;
    int ch=src.characteristics();
    ch&=Spliterator.SIZED|Spliterator.ORDERED|Spliterator.DISTINCT|Spliterator.IMMUTABLE;
    ch|=Spliterator.NONNULL;
    return StreamSupport.stream(new Spliterators.AbstractSpliterator<List<T>>(size, ch)
    {
        private List<T> current;
        @Override
        public boolean tryAdvance(Consumer<? super List<T>> action) {
            if(current==null) current=new ArrayList<>(chunkSize);
            while(current.size()<chunkSize && src.tryAdvance(current::add));
            if(!current.isEmpty()) {
                action.accept(current);
                current=null;
                return true;
            }
            return false;
        }
    }, s.isParallel());
}

Simple test:

简单测试:

chunked(Stream.of(1, 2, 3, 4, 5, 6, 7), 3)
  .parallel().forEachOrdered(System.out::println);

The advantage is that you do not need a full collection of all items for subsequent stream processing, e.g.

优点是您不需要所有项目的完整集合以进行后续流处理,例如

chunked(
    IntStream.range(0, 1000).mapToObj(i -> {
        System.out.println("processing item "+i);
        return i;
    }), 2).anyMatch(list->list.toString().equals("[6, 7]")));

will print:

将打印:

processing item 0
processing item 1
processing item 2
processing item 3
processing item 4
processing item 5
processing item 6
processing item 7
true

rather than processing a thousand items of IntStream.range(0, 1000). This also enables using infinite source Streams:

而不是处理一千件IntStream.range(0, 1000)。这也可以使用无限源Stream

chunked(Stream.iterate(0, i->i+1), 2).anyMatch(list->list.toString().equals("[6, 7]")));


If you are interested in a fully materialized collection rather than applying subsequent Streamoperations, you may simply use the following operation:

如果您对完全物化的集合感兴趣而不是应用后续Stream操作,您可以简单地使用以下操作:

List<Integer> list=Arrays.asList(1, 2, 3, 4, 5, 6, 7);
int listSize=list.size(), chunkSize=2;
List<List<Integer>> list2=
    IntStream.range(0, (listSize-1)/chunkSize+1)
             .mapToObj(i->list.subList(i*=chunkSize,
                                       listSize-chunkSize>=i? i+chunkSize: listSize))
             .collect(Collectors.toList());

回答by Ivan Babanin

You can create your own collector. Something like this:

您可以创建自己的收集器。像这样的东西:

class GroupingCollector<T> implements Collector<T, List<List<T>>, List<List<T>>> {
    private final int elementCountInGroup;

    public GroupingCollector(int elementCountInGroup) {
        this.elementCountInGroup = elementCountInGroup;
    }

    @Override
    public Supplier<List<List<T>>> supplier() {
        return ArrayList::new;
    }

    @Override
    public BiConsumer<List<List<T>>, T> accumulator() {
        return (lists, integer) -> {
            if (!lists.isEmpty()) {
                List<T> integers = lists.get(lists.size() - 1);
                if (integers.size() < elementCountInGroup) {
                    integers.add(integer);
                    return;
                }
            }

            List<T> list = new ArrayList<>();
            list.add(integer);
            lists.add(list);
        };
    }

    @Override
    public BinaryOperator<List<List<T>>> combiner() {
        return (lists, lists2) -> {
            List<List<T>> r = new ArrayList<>();
            r.addAll(lists);
            r.addAll(lists2);
            return r;
        };
    }

    @Override
    public Function<List<List<T>>, List<List<T>>> finisher() {
        return lists -> lists;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
}

And then you can use it in a way like this:

然后你可以像这样使用它:

    List<List<Integer>> collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).collect(new GroupingCollector<>(3));
    System.out.println(collect);

Will print:

将打印:

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

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

回答by mariatsji

A recursive solutionto transform the list to a list-of-lists would also be possible

将列表转换为列表列表的递归解决方案也是可能的

int chunkSize = 2;

private <T> List<List<T>> process(List<T> list) {
    if (list.size() > chunkSize) {
        List<T> chunk = list.subList(0, chunkSize);
        List<T> rest = list.subList(chunkSize, list.size());
        List<List<T>> lists = process(rest);
        return concat(chunk, lists);
    } else {
        ArrayList<List<T>> retVal = new ArrayList<>();
        retVal.add(list);
        return retVal;
    }
}

private <T> List<List<T>> concat(List<T> chunk, List<List<T>> rest) {
    rest.add(0, chunk);
    return rest;
}

回答by ndr_brt

A simple version with java 8 streams api:

带有 java 8 流 api 的简单版本:

static <T> List<List<T>> partition(List<T> list, Integer partitionSize) {
    int numberOfLists = BigDecimal.valueOf(list.size())
        .divide(BigDecimal.valueOf(partitionSize), 0, CEILING)
        .intValue();

    return IntStream.range(0, numberOfLists)
        .mapToObj(it -> list.subList(it * partitionSize, Math.min((it+1) * partitionSize, list.size())))
        .collect(Collectors.toList());
}

回答by Smutje

You could write your own collector finisher, similar to

您可以编写自己的收集器整理器,类似于

final List<String> strings = Arrays.asList("Hello", "World", "I", "Am", "You");
final int size = 3;

final List<List<String>> stringLists = strings.stream()
        .collect(Collectors.collectingAndThen(Collectors.toList(), new Function<List<String>, List<List<String>>>() {
            @Override
            public List<List<String>> apply(List<String> strings) {
                final List<List<String>> result = new ArrayList<>();
                int counter = 0;
                List<String> stringsToAdd = new ArrayList<>();

                for (final String string : strings) {
                    if (counter == 0) {
                        result.add(stringsToAdd);
                    } else {
                        if (counter == size) {
                            stringsToAdd = new ArrayList<>();
                            result.add(stringsToAdd);
                            counter = 0;
                        }
                    }

                    ++counter;
                    stringsToAdd.add(string);
                }

                return result;
            }
        }));

System.out.println("stringLists = " + stringLists); // stringLists = [[Hello, World, I], [Am, You]]