首页 > 解决方案 > 使用 AtomicInteger 在 Stream 中进行索引是否合法?

问题描述

我想得到一个答案,指出为什么下面在一个非常简单的例子中描述的以下想法通常被认为是不好的并且知道它的弱点。

我有一句话,我的目标是让每一秒都变成大写。我对这两种情况的出发点完全相同:

String sentence = "Hi, this is just a simple short sentence";
String[] split = sentence.split(" ");

传统的程序方法是:

StringBuilder stringBuilder = new StringBuilder();
for (int i=0; i<split.length; i++) {
    if (i%2==0) {
        stringBuilder.append(split[i]);
    } else {
        stringBuilder.append(split[i].toUpperCase());
    }
    if (i<split.length-1) { stringBuilder.append(" "); }
}

当想要使用时,由于 lambda 表达式中使用的有效最终或最终变量约束,使用受到限制。我必须使用数组及其第一个也是唯一的索引来使用解决方法,这是在我的问题How to increment a value in Java Stream的第一条评论中建议的。这是示例:

int index[] = {0};
String result = Arrays.stream(split)
    .map(i -> index[0]++%2==0 ? i : i.toUpperCase())
    .collect(Collectors.joining(" "));

是的,这是一个糟糕的解决方案,我在某个我无法找到的问题的评论中听到隐藏在某处的好理由(如果你提醒我其中的一些,如果可能的话,我会投票两次)。但是,如果我使用AtomicInteger- 它有什么不同吗?与以前的方法相比,它是一种没有副作用的好方法吗?

AtomicInteger atom = new AtomicInteger(0);
String result = Arrays.stream(split)
    .map(i -> atom.getAndIncrement()%2==0 ? i : i.toUpperCase())
    .collect(Collectors.joining(" "));

无论对任何人来说它看起来多么丑陋,我都要求描述可能的弱点及其原因。我不在乎性能,而是第二个解决方案的设计和可能的弱点。

请不要将 AtomicInteger 与多线程问题相匹配。我使用了这个类,因为它以我在这个例子中需要的方式接收、递增和存储值。

正如我在回答中经常说的那样,“Java Stream-API”并不是万能的。我的目标是探索并找到这句话适用的边缘,因为我发现最后一个片段与StringBuilder's 片段相比非常清晰、易读和简短。

编辑:是否存在适用于上述片段的任何替代方法以及在使用 Stream-API 进行迭代时需要同时使用项目和索引时的所有问题?

标签: javajava-8iterationjava-stream

解决方案


软件包的文档java.util.stream指出:

通常不鼓励对流操作的行为参数产生副作用,因为它们通常会导致无意中违反无状态要求以及其他线程安全隐患。

[...]

副作用的顺序可能令人惊讶。即使管道被约束以产生与流源的遇到顺序一致的结果(例如,IntStream.range(0,5).parallel().map(x -> x*2).toArray()必须产生[0, 2, 4, 6, 8]),也不能保证映射器函数应用于单个元素的顺序,或者在对给定元素执行任何行为参数的线程。

这意味着元素可能被乱序处理,因此- 解决Stream方案可能会产生错误的结果。

这是(至少对我而言)反对这两种解决方案的杀手锏Stream

通过淘汰的过程,我们只剩下“传统解决方案”了。老实说,我认为这个解决方案没有任何问题。如果我们想摆脱for-loop,我们可以使用 -loop 重写这段代码foreach

boolean toUpper = false; // 1st String is not capitalized
for (String word : splits) {
    stringBuilder.append(toUpper ? word.toUpperCase() : word);
    toUpper = !toUpper;
}

对于流化和(据我所知)正确的解决方案,请查看 Octavian R.'s answer


你的问题。“流的限制”是基于意见的。

问题的答案到此结束。其余的是我的意见,应该这样看待。


在 Octavian R. 的解决方案中,通过 a 创建人工索引集IntStream,然后使用它访问String[]. for对我来说,这比简单的- 或 -循环具有更高的认知复杂性foreach,我认为在这种情况下使用流而不是循环没有任何好处。


推荐阅读