【问题标题】:Proper usage of Streams in Java在 Java 中正确使用 Streams
【发布时间】:2019-04-16 00:36:07
【问题描述】:

我有一个用例,我需要解析键值对(由= 分隔)并将这些键值对放在LinkedHashMap 中。

我想忽略以下类型的Strings

  • 键为空或仅包含空格
  • 值为空或仅包含空格
  • 那些不包含=Strings。

现在,我使用命令式风格和流也解决了它。

以下是 2 个变体:

迭代式解决方案 - for 循环和大量 if

public static Map<String, String> getMap1(String[] array) {
    Map<String, String> map = new LinkedHashMap<>();
    for (int i = 0; i < array.length; i++) {
        String currentString = array[i];
        int index = currentString.indexOf('=');

        // ignoring strings that don't contain '='
        if (index == -1) continue;
        String key = currentString.substring(0, index).trim();
        String value = currentString.substring(index + 1).trim();

        // ignoring strings with empty key or value
        if (key.length() == 0 || value.length() == 0) continue;

        map.put(key, value);
    }
    return map;
}

使用Streams 的解决方案 - 非常干净的代码

public static Map<String, String> getMap(String[] array) {
    return Arrays.stream(array)
            .filter(s -> s.indexOf('=') != -1) // ignore strings that don't contain '='
            .filter(s -> s.substring(0, s.indexOf('=')).trim().length() != 0) // key should be present
            .filter(s -> s.substring(s.indexOf('=') + 1).trim().length() != 0) // value should be present
            .collect(Collectors.toMap(
                    s -> s.substring(0, s.indexOf('=')).trim(),
                    s -> s.substring(s.indexOf('=') + 1).trim(),
                    (first, second) -> second,
                    LinkedHashMap::new));

}

我在这里很担心,因为在使用Streams 时,我多次调用indexOf 方法。 (对于大字符串,我最终可以一次又一次地重新计算相同的东西)。

有没有一种方法可以避免indexOf 方法完成的重新计算,这样代码仍然是干净的。 (我知道谈论干净代码是非常主观的,但我不想打开多个流,循环遍历原始字符串数组,然后预先计算 = 的索引并重新使用它)。

再次将多个filters 合并到一个过滤器中似乎是一种选择,但这会使我的谓词变得非常丑陋。

(这是我在想学习/改进的地方闲逛的结果)。

【问题讨论】:

  • 为什么不使用split 而不是indexOf?正如您所说,此代码还将在array[i] 中为两个= 中断:那些不包含=的字符串

标签: java optimization java-8 java-stream


【解决方案1】:

这个呢:

 String[]  array = {"aaa2=asdas","aaa=asdasd"};

    LinkedHashMap<String, String> aaa = Arrays.stream(array)
            .map(s -> s.split("=", 2))
            .filter(s -> s.length == 2) // ignore strings that don't contain '='
            .peek(s -> { s[0] = s[0].trim(); })
            .peek(s -> { s[1] = s[1].trim(); })
            .filter(s -> s[0].length() != 0) // key should be present
            .filter(s -> s[1].length() != 0) // value should be present
            .collect(Collectors.toMap(
                    s -> s[0],
                    s -> s[1],
                    (first, second) -> second,
                    LinkedHashMap::new));

【讨论】:

  • "aaa2=asdas=asd" 会发生什么?
  • 啊,拆分是我缺少的东西。感谢 Nonika 和 Hadi J :) @HadiJ,是的,它不适用于 aaa2=asdas=asd,但我很高兴 split 能很好地做到这一点。谢谢
  • @LavishKothari 您可以将第二个参数limit 添加到split-Method
  • 这里没有必要滥用peek,就像一个干净的.map(s -&gt; new String[] { s[0].trim(), s[1].trim() }) 一样。但是使用.map(s -&gt; s.trim().split("\\s*=\\s*", 2)) 作为第一个操作将完全消除之后对trim() 的需要。对我来说,一个 .filter(s -&gt; s[0].length() != 0 &amp;&amp; s[1].length() != 0) 比两个 filter 步骤更简单。
  • @Holger 这样做你必须创建一个新的字符串数组,这会带来一些开销。
【解决方案2】:

我会使用 split 而不是 indexOf 和 StringUtils 来检查您的键和值是否不为空。

 public static Map<String, String> getMap(String[] array) {
        return Arrays.stream(array)
                .filter(s -> s.contains("="))
                .map(s -> s.split("="))
                .filter(s -> s.length == 2 && isNotBlank(s[0]) && isNotBlank(s[1]))
                .collect(Collectors.toMap(
                        s -> s[0].trim(),
                        s -> s[1].trim()));
    }

【讨论】:

    猜你喜欢
    • 2021-02-08
    • 1970-01-01
    • 2016-12-12
    • 2020-01-28
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 2017-02-19
    相关资源
    最近更新 更多