Populating an existing Set via Java 8 streams

huangapple go评论58阅读模式
英文:

Populating an existing Set<String> via Java 8 streams

问题

I am new to Java streams and functional programming and I wonder if my question will ever even have a valid use case or it is just not meaningful.
我对Java流和函数式编程很陌生,我想知道我的问题是否有有效的用例,还是根本没有意义。
I have the following code that gives me a Set<String> from an array
我有以下的代码,它从一个数组中给我一个Set<String>。
This gives me an empty dict. If I do it the way I have in second last line, it works. Is there a way to mutate already instantiated dict that was instantiated outside stream that it gets populated in the map function?
这会给我一个空的字典。如果我按照倒数第二行的方式做,它会工作。有没有一种方法可以修改已经在流外实例化的dict,以使其在map函数中得到填充?

英文:

I am new to Java streams and functional programming and I wonder if my question will ever even have a valid use case or it is just not meaningful.
I have the following code that gives me a Set<String> from an array

    public static void main(String[] args) {
        String []arr = {&quot;a&quot;,&quot;abc&quot;,&quot;b&quot;,&quot;cd&quot;};
        Set&lt;String&gt; dict = new HashSet&lt;&gt;();
        Arrays.stream(arr).map(item-&gt;{
            System.out.println(item);
            dict.add(item);
            return dict;
        });

//        dict = Arrays.stream(arr).collect(Collectors.toSet());
        System.out.println(dict);
    }

This gives me an empty dict. If I do it the way I have in second last line, it works. Is there a way to mutate already instantiated dict that was instantiated outside stream that it gets populated in the map function?

答案1

得分: 5

Stream.map() 是一个中间操作,意味着它只是流水线中的一个步骤,但当你调用它时并不会执行任何操作。你需要一个终端操作来让流开始工作。在你的情况下,你可以使用 forEachOrdered()

Arrays.stream(arr)
        .peek(System.out::println)
        .forEachOrdered(dict::add);

注意:我假设你需要修改现有的集合而不是生成一个新的集合。否则,可以像其他答案中建议的那样使用 collect()

英文:

Stream.map() is an intermediate operation, meaning it only stages a step of the pipeline, but it doesn't do anything when you call it. You need a terminal operation to put the stream to work. In your case, you can use forEachOrdered():

Arrays.stream(arr)
        .peek(System.out::println)
        .forEachOrdered(dict::add);

Note: I'm assuming there's a reason you need to modify an existing set rather than produce a new one. Otherwise, just use collect() as suggested in the other answer.

答案2

得分: 3

  1. 在问题被编辑之前,已注释部分无法编译,因为 dict 必须是 final 或者 effectively final。有关更多信息,请参见此答案
  2. 使用 Stream API 的少数规则之一是避免副作用。java.util.stream 包的 JavaDoc 用示例很好地描述了这一点:
    > 通常不鼓励在流操作的行为参数中使用副作用,因为这往往会导致无意中违反无状态要求,以及其他线程安全风险。
  3. 每个 Stream 的要点是通过诸如过滤、映射、归约或高级收集等操作返回新的集合/值。它从未旨在修改现有集合。将数组 arr 转换为集合的正确方式是:
    Set&lt;String&gt; dict = Arrays.stream(arr)
          .peek(item -&gt; System.out.println(item)) // 用于调试的可选项
          .collect(Collectors.toSet());
    
英文:
  1. Before the question got edited, the commented part didn't compile because dict must be either final or effectively final. See this answer for more information.
  2. One of the few rules of using Stream API is to avoid side effects. The JavaDoc for java.util.stream package describes it well with examples:
    > Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement, as well as other thread-safety hazards.
  3. The gist of each Stream is to return a new collection/value through operations such as filtering, mapping, reducing, or advanced collecting. It was never meant to modify an existing one. The correct way how to transform the array arr into a set is:
    Set&lt;String&gt; dict = Arrays.stream(arr)
          .peek(item -&gt; System.out.println(item)) // optional for debugging
          .collect(Collectors.toSet());
    

huangapple
  • 本文由 发表于 2023年5月25日 12:44:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76329000.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定