在Java 8中是否有类似于Grails中collate方法的等价方法?

5

我想把一个列表分成几个子列表。在Groovy中,我可以很容易地做到这一点:

def letters = 'a'..'g'

assert letters.collate(3) == [['a', 'b', 'c'], ['d', 'e', 'f'], ['g']]

在Java 8中是否有类似的功能?我已经查看了Collectors,但它似乎有点复杂。我真的只想将列表中的项目分组为x。


你想将一个 List<T> 转换成一个 List<List<T>>,其中除了最后一个内部列表外,所有内部列表的大小都是一些常量大小吗? - Mithun Sasidharan
4个回答

3
您可以查看 Guava 的列表分区 API :Partition
public static <T> java.util.List<java.util.List<T>> partition(java.util.List<T> list, int size)

返回列表中连续的子列表,每个子列表的大小相同(最后一个子列表可能较小)。


2
这个怎么样?
char start = 'a';
char last = 'g';
int n = 3;

List<Character> letters = IntStream.rangeClosed(start, last)
                                   .mapToObj(it -> (char) it)
                                   .collect(toList());

List<List<Character>> result = IntStream.range(0, (letters.size() + n - 1) / n)
               .map(i -> i * n)
               .mapToObj(i -> letters.subList(i, Math.min(i + n, letters.size())))
               .collect(toList());

OR

List<List<Character>> result = IntStream.range(0, letters.size()).boxed().
          collect(collectingAndThen(
               groupingBy(i -> i / n, mapping(letters::get, toList())),
               map -> new ArrayList<>(map.values())
          ));

2

这个问题以前已经讨论过,但我现在找不到了,所以这里有一个简洁的方法:

private static <T> Collector<T, ?, List<List<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 are both 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<List<T>> finisher() {
            return list;
        }

    }
    return Collector.of(Acc::new, Acc::add, Acc::merge, Acc::finisher);
}

使用方法:

List<List<Integer>> list = Arrays.asList(1, 3, 4, 5, 9, 8, 7)
            .stream()
            .parallel()
            .collect(partitioning(3));

问题在于通过combiner,这对并行流实现了很好的效果。此外,少量代码并不意味着更好或更高效的解决方案。


0

试试我的库abacus-common

CharStream.rangeClosed('a', 'g').split(3).println();
// print out: [[a, b, c], [d, e, f], [g]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接