有没有办法使这个流更有效率?

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

Any way to make this stream more efficient?

问题

如何在不向新的ArrayList中添加值的情况下进行优化,仅更新原始列表?

String filterval = filter.toLowerCase();
ArrayList<String> filtredArr = new ArrayList<String>();

listArray.forEach(
    valueText -> {
        String val = valueText.toLowerCase();
        if (val.startsWith(filterval) || val.contains(filterval))
            filtredArr.add(valueText);
        else {
            Arrays.stream(valueText.split(" ")).forEach(
                singleWord -> {
                    String word = singleWord.toLowerCase();
                    if (word.startsWith(filterval) || word.contains(filterval))
                        filtredArr.add(valueText);
                }
            );
        }
    });
英文:

How would one optimise this without adding value into the new ArrayList instead just having the original list updated?

 String filterval = filter.toLowerCase();
 ArrayList&lt;String&gt; filtredArr = new ArrayList&lt;String&gt;();

                        listArray.forEach(
                                valueText -&gt; {
                                        String val = valueText.toLowerCase();
                                    if (val.startsWith(filterval) || val.contains(filterval))
                                        filtredArr.add(valueText);
                                    else {
                                        Arrays.stream(valueText.split(&quot; &quot;)).forEach(
                                                singleWord -&gt; {
                                                    String word = singleWord.toLowerCase();
                                                    if(word.startsWith(filterval) || word.contains(filterval))
                                                        filtredArr.add(valueText);
                                                }
                                        );
                                    }
                                });

答案1

得分: 0

使用流(Streams)时,最好不要在流迭代过程中修改流的源。有关可参考此链接

关于代码的可读性和对流操作的惯用使用,你的代码几乎和普通的 for 循环没有区别(如果你只是用流来执行 forEach 操作,我建议你将其改为普通的 for 循环)。但是你可以将其修改为使用一系列更短、更“原子”的流操作,如以下示例所示。

  • 获取至少包含一个单词包含 filterval 的字符串列表:

    List<String> filtered = listArray.stream()
                                   .filter(str -> str.toLowerCase().contains(filterval))
                                   .collect(Collectors.toList());
    
  • 获取至少包含一个单词 filterval 开头的字符串列表:

    List<String> filtered =
          listArray.stream()
                   .filter(str -> Arrays.stream(str.split(" "))
                                        .map(String::toLowerCase)
                                        .anyMatch(word -> word.startsWith(filterval)))
                   .collect(Collectors.toList());
    
  • 获取包含过滤值的单词(在任何字符串中)的列表:

    List<String> filteredWords = listArray.stream()
                                        .map(String::toLowerCase)
                                        .flatMap(str -> Arrays.stream(str.split(" ")))
                                        .filter(word -> word.contains(filterval))
                                        .collect(Collectors.toList());
    

(我假设 listArray,在你的代码片段中没有展示,是一个 List<String>。)

注释

  • 条件 val.startsWith(filterval) || val.contains(filterval)val.contains(filterval) 完全等价。原因是,如果一个字符串以 filterval 开头,那么它一定也包含它;一个蕴含着另一个。
  • 无需计算单词的小写版本,因为你已经将整个字符串转换为小写(因此其中的任何单词也都是小写的)。
  • 我们可以将 split 应用于所有单词和以空格分隔的字符串,然后使用 flatMap 来“连接”单词序列,从而不再需要分别处理单个单词和空格分隔的字符串。

最小、完整、可验证示例

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class Main {
    public static void main(String[] args) {
        String filterval = "ba";
        List<String> listArray = List.of("foo", "BAR", "spam EGGS ham bAt", "xxbazz");


        List<String> filtered1 = listArray.stream()
                                          .filter(str -> str.toLowerCase().contains(filterval))
                                          .collect(Collectors.toList());

        List<String> filtered2 =
                listArray.stream()
                         .filter(str -> Arrays.stream(str.split(" "))
                                              .map(String::toLowerCase)
                                              .anyMatch(word -> word.startsWith(filterval)))
                         .collect(Collectors.toList());

        List<String> filtered3 = listArray.stream()
                                          .map(String::toLowerCase)
                                          .flatMap(str -> Arrays.stream(str.split(" ")))
                                          .filter(word -> word.contains(filterval))
                                          .collect(Collectors.toList());

        System.out.println(Arrays.toString(filtered1.toArray()));
        System.out.println(Arrays.toString(filtered2.toArray()));
        System.out.println(Arrays.toString(filtered3.toArray()));
    }
}

输出:

[BAR, spam EGGS ham bAt, xxbazz]
[BAR, spam EGGS ham bAt]
[bar, bat, xxbazz]
英文:

When using streams, it is best not to modify the source of the stream while the latter iterates over the former. (See this for instance.)

Regarding readability and making idiomatic use of stream operations, your code is almost indistinguishable from a plain old for loop (and I would advise you to change it to that if you only use streams to do a forEach, but you could modify it to use a chain of shorter and more "atomic" stream operations, as in the following examples.

  • To get the list of strings in which at least one word contains filterval:

    List&lt;String&gt; filtered = listArray.stream()
                                   .filter(str -&gt; str.toLowerCase().contains(filterval))
                                   .collect(Collectors.toList());
    
  • To get the list of strings in which at least one word starts with filterval:

    List&lt;String&gt; filtered =
          listArray.stream()
                   .filter(str -&gt; Arrays.stream(str.split(&quot; &quot;))
                                        .map(String::toLowerCase)
                                        .anyMatch(word -&gt; word.startsWith(filterval)))
                   .collect(Collectors.toList());
    
  • To get the list of words (in any of the strings) that contain the filter value:

    List&lt;String&gt; filteredWords = listArray.stream()
                                        .map(String::toLowerCase)
                                        .flatMap(str -&gt; Arrays.stream(str.split(&quot; &quot;)))
                                        .filter(word -&gt; word.contains(filterval))
                                        .collect(Collectors.toList());
    

(I'm assuming listArray, which you don't show in your code snippet, is a List&lt;String&gt;.)

Notes

  • The condition val.startsWith(filterval) || val.contains(filterval) is completely equivalent to val.contains(filterval). The reason is, if a string starts with filterval, it must also mean that it contains it; one implies the other.
  • There's no need to compute the lowercase versions of single words since you've already lowercased the whole string (so any words within it will also be lowercase).
  • Instead of treating single words and space-separated strings separately, we can apply split to all of them and then "concatenate" the sequences of words by using filterMap.

Minimal, complete, verifiable example

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class Main {
    public static void main(String[] args) {
        String filterval = &quot;ba&quot;;
        List&lt;String&gt; listArray = List.of(&quot;foo&quot;, &quot;BAR&quot;, &quot;spam EGGS ham bAt&quot;, &quot;xxbazz&quot;);


        List&lt;String&gt; filtered1 = listArray.stream()
                                          .filter(str -&gt; str.toLowerCase().contains(filterval))
                                          .collect(Collectors.toList());

        List&lt;String&gt; filtered2 =
                listArray.stream()
                         .filter(str -&gt; Arrays.stream(str.split(&quot; &quot;))
                                              .map(String::toLowerCase)
                                              .anyMatch(word -&gt; word.startsWith(filterval)))
                         .collect(Collectors.toList());

        List&lt;String&gt; filtered3 = listArray.stream()
                                          .map(String::toLowerCase)
                                          .flatMap(str -&gt; Arrays.stream(str.split(&quot; &quot;)))
                                          .filter(word -&gt; word.contains(filterval))
                                          .collect(Collectors.toList());

        System.out.println(Arrays.toString(filtered1.toArray()));
        System.out.println(Arrays.toString(filtered2.toArray()));
        System.out.println(Arrays.toString(filtered3.toArray()));
    }
}

Output:

[BAR, spam EGGS ham bAt, xxbazz]
[BAR, spam EGGS ham bAt]
[bar, bat, xxbazz]

huangapple
  • 本文由 发表于 2020年7月27日 16:42:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/63111650.html
匿名

发表评论

匿名网友

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

确定