使用Java 8流API计算连续相同对象的数量

使用Java 8流API计算连续相同对象的数量,java,stream,java-stream,Java,Stream,Java Stream,我想用Java8流一个接一个地计算相同对象的数量。我该怎么做?如果我有一份 3, 3, 5, 5, 5, 6, 3, 3 我希望结果是 [3-2] [5-3] [6-1] [3-2] 我在没有Java 8流的情况下的天真尝试: private static List<ValueCount> counteSameValueInRow(List<Integer> Values) { List<ValueCount> result = new Array

我想用Java8流一个接一个地计算相同对象的数量。我该怎么做?如果我有一份

3, 3, 5, 5, 5, 6, 3, 3
我希望结果是

[3-2] [5-3] [6-1] [3-2]
我在没有Java 8流的情况下的天真尝试:

private static List<ValueCount> counteSameValueInRow(List<Integer> Values) {
    List<ValueCount> result = new ArrayList<ValueCount>();
    ValueCount valueCount = null;

    for (int value: Values) {
        if (valueCount == null) {
            valueCount = new ValueCount(value);
        } else if (valueCount.value == value){
            valueCount.numberof++;
        } else {
            result.add(valueCount);
            valueCount = new ValueCount(value);
        }
    }
    result.add(valueCount);
    return result;
}
private静态列表counteSameValueInRow(列表值){
列表结果=新建ArrayList();
ValueCount ValueCount=null;
for(int值:值){
如果(valueCount==null){
valueCount=新的valueCount(值);
}else if(valueCount.value==值){
valueCount.numberoff++;
}否则{
结果。添加(valueCount);
valueCount=新的valueCount(值);
}
}
结果。添加(valueCount);
返回结果;
}

您所做的工作与流上的
收集相当。你将每个数字汇总成一个“组”列表
Collectors.groupingBy()
会出现在脑海中,但这会将整个列表中的数字进行分组,也就是说,只需计算每个数字的出现次数。使用
Stream.collect(Supplier、BiConsumer、BiConsumer)
方法实现自定义收集,您可以执行如下操作:

List<Integer> values = Arrays.asList(3, 3, 5, 5, 5, 6, 3, 3);

values.stream().collect(LinkedList<List<Integer>>::new, (list, value) -> {
    if (list.isEmpty() || !list.getLast().get(0).equals(value))
    {
        list.add(new ArrayList<>());
    }
    list.getLast().add(value);
}, (list1, list2) -> {
    if (list1.getLast().get(0).equals(list2.getFirst().get(0)))
    {
        list1.getLast().addAll(list2.getFirst());
        list2.removeFirst();
    }
    list1.addAll(list2);
}).forEach(group -> System.out.println("[" + group.get(0) + "-" + group.size() + "]"));
List values=Arrays.asList(3,3,5,5,6,3,3);
values.stream().collect(LinkedList::new,(列表,值)->{
if(list.isEmpty()| |!list.getLast().get(0).equals(值))
{
添加(新的ArrayList());
}
list.getLast().add(value);
},(列表1,列表2)->{
if(list1.getLast().get(0).equals(list2.getFirst().get(0)))
{
list1.getLast().addAll(list2.getFirst());
清单2.removeFirst();
}
列表1.addAll(列表2);
}).forEach(group->System.out.println(“[”+group.get(0)+“-”+group.size()+“]));
请注意,我使用了
ArrayList
s来收集重复项。您可以将
ValueCount
类用于此目的,这可能会使其更具可读性

本例中的流并没有改善代码的可读性,而是支持使用并行处理。查看collect方法的第三个参数。在并行处理流的情况下,该方法结合了两个中间结果


要并行尝试,请将
stream()
替换为
parallelStream()
,并在第三个参数的lambda中放置一个
sysout
,以查看两个中间结果何时合并。请注意,并行处理只会在列表非常大的情况下对您有利。

在Malte Hartwig的建议之后,我进行了重构以获得更好的可读性。结果如下

 public static void main(String[] args) {
    List<Integer> values = Arrays.asList(3, 3, 5, 5, 5, 6, 3, 3);

    BiPredicate<Integer, Integer> predicate = (value1, value2) -> value1.equals(value2);

    Supplier<BiConsumer<LinkedList<ValueCount>, Integer>> accumulator = 
        () -> (list, value) -> {
        if (list.isEmpty() || !predicate.test(list.getLast().getFirstValue(), value)) {
            list.add(new ValueCount());
        }
        list.getLast().add(value);
    };

    Supplier<BiConsumer<LinkedList<ValueCount>, LinkedList<ValueCount>>> combiner = () -> (list1, list2) -> {
        if (list1.getLast().getFirstValue().equals(list2.getFirst().getFirstValue())) {
            list1.getLast().addAll(list2.getFirst());
            list2.removeFirst();
        }
        list1.addAll(list2);
    };

    values.stream().collect(LinkedList::new, accumulator.get(), combiner.get())
            .forEach(group -> System.out.println(group));
}

private static class ValueCount {
    private List<Integer> lista = new ArrayList<>();

    public String toString() { return "[" + lista.get(0) + "-" + lista.size() + "]";}
    public void add(Integer value) { lista.add(value);}
    public Integer getFirstValue() { return lista.get(0);}
    public void addAll(ValueCount first) { lista.addAll(first.getAll());}
    private Collection<? extends Integer> getAll() { return lista;}
}
publicstaticvoidmain(字符串[]args){
列表值=数组.asList(3,3,5,5,5,6,3,3);
双预测谓词=(value1,value2)->value1.equals(value2);
供应商累加器=
()->(列表,值)->{
if(list.isEmpty()| |!predicate.test(list.getLast().getFirstValue(),value)){
添加(新的ValueCount());
}
list.getLast().add(value);
};
供应商组合器=()->(列表1,列表2)->{
如果(list1.getLast().getFirstValue().equals(list2.getFirst().getFirstValue())){
list1.getLast().addAll(list2.getFirst());
清单2.removeFirst();
}
列表1.addAll(列表2);
};
values.stream().collect(LinkedList::new,accumulator.get(),combiner.get())
.forEach(组->系统输出打印项次(组));
}
私有静态类ValueCount{
private List lista=new ArrayList();
公共字符串toString(){return“[”+lista.get(0)+”-“+lista.size()+”];}
public void add(整数值){lista.add(值);}
公共整数getFirstValue(){return lista.get(0);}
public void addAll(ValueCount first){lista.addAll(first.getAll());}

private CollectionStreams根本不适合这种情况。我建议您坚持使用现有的解决方案。