在Java 8中使用流的方式合并列表中重复的数字,是否有解决方法?

6

例如#1

[1, 1, 1, 2, 22, 35, 35, 120, 320] ==>> [3, 2, 22, 70, 120, 320]

请注意,连续重复的1和35被合并为3和70。

例如#2

[1,1,3,1,1] ==>> [2,3,2]

Coalesce 的意思是聚合在一起,形成一个单一的、更大的总和或价值。他使用得很恰当。 - Jason
coalesce的意思是将多个项目合并为一个单元,对吗?在这里,我想问一下是否可以将重复的数字合并为一个数字(即这些数字的总和)。 - Aniruth N
1
但是它对于这个不起作用:[0,0,0,0, 1, 1, 1, 0, 0, 0] => [0, 3, 0] - Aniruth N
3
[1,1,3,1,1] 应该得到的结果是 [2,3,2] 还是 [4,3](或者其他什么结果)? - Pshemo
它将是[2, 3, 2] - Aniruth N
3个回答

8
Stream.of(1, 1, 1, 2, 22, 35, 35, 120, 320)
          .collect(Collectors.toMap(
              Function.identity(),
              Function.identity(),
              Integer::sum,
              LinkedHashMap::new
          ))
          .values()
          .forEach(System.out::println);

如果您发布评论,您需要一个自定义收集器:

在此情况下,您需要一个自定义收集器。

static class Custom implements Collector<Integer, List<Integer>, List<Integer>> {

    private Integer match;

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

    @Override
    public BiConsumer<List<Integer>, Integer> accumulator() {
        return (list, x) -> {
            int lastIndex = list.size() - 1;
            if (match != null && match.equals(x)) {
                list.set(lastIndex, list.get(lastIndex) + x);
            } else {
                match = x;
                list.add(x);
            }
        };
    }

    @Override
    public BinaryOperator<List<Integer>> combiner() {
        return (left, right) -> {
            throw new RuntimeException("Not for parallel");
        };
    }

    @Override
    public Function<List<Integer>, List<Integer>> finisher() {
        return Function.identity();
    }

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

使用方法如下:

public static void main(String[] args) {
    Stream.of(1, 1, 3, 1, 1)
          .collect(new Custom())
          .forEach(System.out::println);
}

4
但是对于这个不起作用:[0,0,0,0, 1, 1, 1, 0, 0, 0] => [0, 3, 0] - Aniruth N

3
为什么要使用流(streams),而不是用简单的for循环和if-else块呢?
   List<Integer> list = List.of(0, 0, 0, 0, 1, 1, 1, 0, 0, 0);
   List<Integer> result = new ArrayList<>();
   Integer curr = list.get(0);
   Integer sum = 0;
   for(Integer i : list){
       if(i.equals(curr)){
           sum += i;
       }
       else{
           result.add(sum);
           sum = i;
           curr = i;
       }
   }
   result.add(sum);
   System.out.println(result);

1
我在想是否可以使用一些花哨的reduce函数或其他方法来用几行代码实现。 - Aniruth N
1
在我看来,当您需要比较特定位置的元素时,流是可怕的用例。这个for循环更加简洁。 - Arnaud Denoyelle
2
@AniruthN 如果你只需要计算出现次数,你可以使用 input.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) 然后对其进行迭代。但是 1) 条目将不会排序 2) 它不适用于像 [0, 0, 0, 0, 1, 1, 1, 0, 0, 0] 这样的条目。 - Arnaud Denoyelle
1
不幸的是,流和正则表达式很相似。它们都被设计用于支持许多不同的情况,其中使用循环和简单解析器更加高效,并且通常并不难编写。 - WJS

2

这里提供一种使用栈的方法。

public static List<Integer> coalesce(List<Integer> list) {
    Stack<Integer> stack = new Stack<>();
    stack.addAll(list);
    List<Integer> sums = new ArrayList<>();
    int sum = 0;
    while (!stack.isEmpty()) {
       int val = stack.pop();
       sum += val;
       if (!stack.isEmpty() && stack.peek() != val) {
          sums.add(0,sum);
          sum = 0;
       }
    }
    sums.add(0,sum);
    return sums;
}

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