繁体   English   中英

在 Java 8 Stream API 中连续计算相同的项目

[英]Count the same items in a row in Java 8 Stream API

我有一颗豆子和一条溪流

public class TokenBag {
    private String token;
    private int count;
    // Standard constructor and getters here
}
Stream<String> src = Stream.of("a", "a", "a", "b", "b", "a", "a");

并希望对返回另一个 TokenBag 对象流的流应用一些中间操作。 在此示例中,必须有两个:("a", 3)、("b", 3) 和 ("a", 2)。

请认为这是一个非常简单的例子。 实际上,将有比仅仅连续计算相同值更复杂的逻辑。 实际上,我尝试设计一个简单的解析器,它接受一个标记流并返回一个对象流。

另请注意,它必须保持一个流(没有中间累积),并且在此示例中,它必须真正连续计算相同的值(它与分组不同)。

将感谢您对此任务解决方案的一般方法的建议。

Map<String, Long> result = src.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(result);

这将给出所需的输出

a=4, b=3

然后,您可以继续遍历 map 并创建TokenBag的对象。

    Stream<String> src = Stream.of("a", "a", "a", "a", "b", "b", "b");

// collect to map 
    Map<String, Long> counted = src
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

// collect to list 
    List<TokenBag> tokenBags = counted.entrySet().stream().map(m -> new TokenBag(m.getKey(), m.getValue().intValue()))
            .collect(Collectors.toList());

首先将其分组到 Map,然后将条目映射到 TokenBag:

Map<String, Long> values = src.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
List<TokenBag> tokenBags = values.entrySet().stream().map(entry -> {
    TokenBag tb = new TokenBag();
    tb.setToken(entry.getKey());
    tb.setCount(entry.getValue().intValue());
    return tb;
}).collect(Collectors.toList());

您需要将流转换为Spliterator ,然后将此拆分器调整为自定义的拆分器,根据您的逻辑部分减少一些元素(在您的示例中,它需要计算相等的元素,直到出现不同的元素)。 然后,您需要将拆分器转回新流。

请记住,这不能 100% 偷懒,因为您需要急切地使用支持流中的一些元素,以便为新流创建新的TokenBag元素。

这是自定义拆分器的代码:

public class CountingSpliterator
        extends Spliterators.AbstractSpliterator<TokenBag>
        implements Consumer<String> {

    private final Spliterator<String> source;
    private String currentToken;
    private String previousToken;
    private int tokenCount = 0;
    private boolean tokenHasChanged;

    public CountingSpliterator(Spliterator<String> source) {
        super(source.estimateSize(), source.characteristics());
        this.source = source;
    }

    @Override
    public boolean tryAdvance(Consumer<? super TokenBag> action) {
        while (source.tryAdvance(this)) {
            if (tokenHasChanged) {
                action.accept(new TokenBag(previousToken, tokenCount));
                tokenCount = 1;
                return true;
            }
        }
        if (tokenCount > 0) {
            action.accept(new TokenBag(currentToken, tokenCount));
            tokenCount = 0;
            return true;
        }
        return false;
    }

    @Override
    public void accept(String newToken) {
        if (currentToken != null) {
            previousToken = currentToken;
        }
        currentToken = newToken;
        if (previousToken != null && !previousToken.equals(currentToken)) {
            tokenHasChanged = true;
        } else {
            tokenCount++;
            tokenHasChanged = false;
        }
    }
}

所以这个拆分器扩展Spliterators.AbstractSpliterator并且还实现了Consumer 代码相当复杂,但其想法是它将源拆分器中的一个或多个令牌调整为TokenBag的一个实例。

对于来自源拆分器的每个接受的令牌,该令牌的计数都会递增,直到令牌更改。 此时,使用令牌和计数创建了一个TokenBag实例,并立即推送到Consumer<? super TokenBag> action Consumer<? super TokenBag> action参数。 此外,计数器重置为1 accept方法中的逻辑处理令牌更改、边界情况等。

以下是您应该如何使用此拆分器:

Stream<String> src = Stream.of("a", "a", "a", "b", "b", "a", "a");

Stream<TokenBag> stream = StreamSupport.stream(
        new CountingSpliterator(src.spliterator()),
        false); // false means sequential, we don't want parallel!

stream.forEach(System.out::println);

如果您在TokenBag中覆盖toString() ,则输出为:

TokenBag{token='a', count=3}
TokenBag{token='b', count=2}
TokenBag{token='a', count=2}

关于并行性的说明:我不知道如何并行化这个部分减少任务,我什至不知道它是否可能。 但如果是这样,我怀疑它会产生任何可衡量的改进。

创建地图,然后将地图收集到列表中:

Stream<String> src = Stream.of("a", "a", "a", "a", "b", "b", "b");
Map<String, Long> m = src.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
m.entrySet().stream().map(e -> new TokenBag(e.getKey(), e.getValue().intValue())).collect(Collectors.toList());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM