计算值在集合中更改的次数

时间:2016-07-27 19:54:54

标签: java java-stream

例如,假设我有一个对象列表

List<Integer> integers = new ArrayList<Integer>() {
            {
                add(Integer.valueOf(1));
                add(Integer.valueOf(1));
                add(Integer.valueOf(2)); // changed
                add(Integer.valueOf(2));
                add(Integer.valueOf(2));
                add(Integer.valueOf(3)); // changed
                add(Integer.valueOf(3));
                add(Integer.valueOf(1)); // changed
                add(Integer.valueOf(2)); // changed
            }
        };

上面的例子应该返回数字4,因为该值与之前的数字不同4次。

我目前有一种繁琐且难以维护的方式,包括循环遍历项目并跟踪先前的值和累加器以存储转换次数。

我很想知道是否有一种java 8流方式来解决这个问题,或者是一个更优雅的解决方案,而不仅仅是跋涉整个集合并维护前一个值的副本。

谢谢!

3 个答案:

答案 0 :(得分:3)

只需将元素与前一个元素进行比较:

int count = 0;
for (int i = 1; i < integers.size(); ++i) {
  if (!integers.get(i).equals(integers.get(i-1))) {
    ++count;
  }
}
System.out.println(count);

或使用Iterator

int count = 0;
if (!integers.isEmpty()) {
  Iterator<?> it = integers.iterator();
  Object prev = it.next();
  while (it.hasNext()) {
    Object curr = it.next();
    if (!curr.equals(prev)) {
      ++count;
    }
    prev = curr;
  }
}
System.out.println(count);

答案 1 :(得分:0)

是的,可以使用reduce操作。但是,我不会编写这样的代码,因为这比迭代解决方案更难读。 Java Stream API并非设计为跨元素的操作,因此不要使用此代码。

 static class ReduceContext {
    public final Integer previous;
    public final Integer result;

    public ReduceContext(Integer previous, Integer result) {
      this.previous = previous;
      this.result = result;
    }
  }

  public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 1, 2, 2, 2, 3, 3, 1, 2);

    ReduceContext reduction = list.stream().reduce(
        new ReduceContext(null, null),
        (ctx, current) -> current.equals(ctx.previous) ? ctx : 
              new ReduceContext(current, ctx.result != null ? ctx.result + 1 : 0), 
        (x, y) -> new ReduceContext(null, x.result + y.result));

    System.out.println(reduction.result); // prints 4
  }

如果前一个值与当前的流元素不同,那么诀窍就是减少不断更新的上下文。此实现也将通过并行缩减来打破,并且在合并器上也存在一些令人讨厌的边缘情况。

答案 2 :(得分:0)

如果你必须使用一个标准的列表,但是如果你想使用一些Java 8,请尝试使用ObservableList。通过下面的示例,我没有将其设置为能够一次添加整个值列表,而是一次添加一个值。

ObservableList<Integer> list = FXCollections.observableArrayList();
    list.addListener((ListChangeListener.Change<? extends Integer> c) -> {
        while( c.next() ) {
            if( c.wasAdded() ) {
                // Make sure the list is not equal to 1, or you'll compare a null and get an error
                // Then get the added sub list (the new value you're adding) and compare it to the second to last
                if( c.getList().size() > 1 && c.getAddedSubList().get(0) != c.getList().get( c.getTo()-2 ) ) {
                    System.out.println( "Change!" );
                }
            }
        }
    });

list.add( 1 );
list.add( 1 );
list.add( 2 );
list.add( 3 );
list.add( 1 );
list.add( 2 );
list.add( 3 );
list.add( 3 );

输出:

Change!
Change!
Change!
Change!
Change!