Sure, here’s the translation: Java – 如何改进这个函数(Java 8 streams)

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

Java - how to improve on this function (Java 8 streams)

问题

我已编写了一个使用Java 8流查找字符串中唯一字符的函数,基于这个示例这里

对我来说,这似乎不太直观,也许是因为我仍在学习流。有没有办法让这段代码更易读?

以下是代码:

public static void main(String[] args) {

    String str = "aabcddeffg";

    char[] charArray = str.toCharArray();

    List<String> strList = new ArrayList<>();
    for (int i = 0; i < charArray.length; i++) {
        String myChar = String.valueOf(charArray[i]);
        strList.add(myChar);
    }

    Map<String, Long> myMap =
            strList.stream().
                    collect(
                            Collectors.groupingBy(
                                    Function.identity(), Collectors.counting()
                            )
                    );

    myMap.forEach((k, v) -> {
        if (v == 1) {
            System.out.println(k);
        }
    });

}
}
英文:

I've coded a function that finds the unique characters in a string using Java 8 streams, based on an example here.

To me, it's pretty non-intuitive, maybe because I'm still learning streams. Is there any way to make this more readable?

Here's the code:

    public static void main(String[] args) {

        String str = &quot;aabcddeffg&quot;;

        char[] charArray = str.toCharArray();

        List&lt;String&gt; strList = new ArrayList&lt;&gt;();
        for(int i=0; i&lt; charArray.length; i++){
            String myChar = String.valueOf(charArray[i]);
            strList.add(myChar);
        }

        Map&lt;String, Long&gt; myMap =
                strList.stream().
                        collect(
                                Collectors.groupingBy(
                                        Function.identity(),Collectors.counting()
                                )
                        );

        myMap.forEach((k, v) -&gt; {
            if (v == 1) {
                System.out.println(k);
            }
        });

    }
}

答案1

得分: 1

用于计算实际频率的代码已经非常简明了!

Map<Integer, Long> charFrequency = strList.stream() //Stream<String>
        .flatMapToInt(String::chars)  //Stream<IntStream> -> IntStream
        .boxed() //IntStream -> Stream<Integer>
        .collect(Collectors.groupingBy( //Map<K, V>
                Function.identity(), //K == 我们的整数
                Collectors.counting() //V == 它们的数量
        ));

charFrequency.entrySet().stream() //Stream<Map.Entry<Integer, Long>>
        .filter(ent -> ent.getValue() == 1) //只保留值为1的条目
        .mapToInt(Map.Entry::getKey) //Stream<Entry> -> IntStream
        .forEach(c -> {
            System.out.println("找到唯一字符:" + ((char) c));
        });

对于单个字符串进行相同操作甚至更简单(避免了类型转换):

Map<Integer, Long> charFrequency = someString.chars() //Stream<Integer>
        .collect(Collectors.groupingBy( //Map<K, V>
                Function.identity(), //K == 我们的整数
                Collectors.counting() //V == 它们的数量
        ));

为此,我建议确保你的代码简洁一致且易读。保持一致的缩进,对每一步流程进行逐行注释,比如这样做:

编辑:我保留了下面(旧的)答案,仅供原问题提问者参考,但它并没有回答实际问题。

嗯,总有Stream#distinct方法。

计算“不同”(而不是_唯一的_)字符:

List<Integer> distinctChars = strList.stream() //Stream<String>
        .flatMapToInt(String::chars)  //Stream<IntStream> -> IntStream
        .distinct() //唯一的 IntStream
        .boxed() //唯一的 Stream<Integer>
        .collect(Collectors.toList()); //List<Integer>

distinctChars.forEach(c -> {
    System.out.println("找到不同字符:" + ((char) (int) c));
});

如果你想避免收集,也可以避免所有与类型装箱相关的麻烦:

strList.stream() //Stream<String>
        .flatMapToInt(String::chars)  //Stream<IntStream> -> IntStream
        .distinct() //唯一的 IntStream
        .forEach(c -> {
            System.out.println("找到不同字符:" + ((char) c));
        });
英文:

For calculating the actual frequencies, you're pretty much at a minimal example already!

Map&lt;Integer, Long&gt; charFrequency = strList.stream() //Stream&lt;String&gt;
            .flatMapToInt(String::chars)  //Stream&lt;IntStream&gt; -&gt; IntStream
            .boxed() //IntStream -&gt; Stream&lt;Integer&gt;
            .collect(Collectors.groupingBy( //Map&lt;K, V&gt;
                    Function.identity(), //K == our integers
                    Collectors.counting() //V == the number of them
            ));

charFrequency.entrySet().stream() //Stream&lt;Map.Entry&lt;Integer, Long&gt;&gt;
            .filter(ent -&gt; ent.getValue() == 1) //only entries with value of 1
            .mapToInt(Map.Entry::getKey) //Stream&lt;Entry&gt; -&gt; IntStream
            .forEach(c -&gt; {
                System.out.println(&quot;Found unique character: &quot; + ((char) c));
            });

And for doing it for a single string, it's even easier (you save the conversions):

Map&lt;Integer, Long&gt; charFrequency = someString.chars() //Stream&lt;Integer&gt;
            .collect(Collectors.groupingBy( //Map&lt;K, V&gt;
                    Function.identity(), //K == our integers
                    Collectors.counting() //V == the number of them
            ));

To that end, I would ensure that your code is simply consistent and readable. Use consistent indentation and comment how the stream steps per line, for example.

Edit: I've left the below (old) answer just to be informative for the OP, but it doesn't answer the actual question.

Well, there's always Stream#distinct.

Calcuating the "distinct" (not unique) characters:

List&lt;Integer&gt; distinctChars = strList.stream() //Stream&lt;String&gt;
        .flatMapToInt(String::chars)  //Stream&lt;IntStream&gt; -&gt; IntStream
        .distinct() //unique IntStream
        .boxed() //unique Stream&lt;Integer&gt;
        .collect(Collectors.toList()); //List&lt;Integer&gt;

distinctChars.forEach(c -&gt; {
    System.out.println(&quot;Found distinct char: &quot; + ((char) (int) c));
});

If you want to avoid collecting it, you can also avoid all the hassle around the type boxing:

strList.stream() //Stream&lt;String&gt;
        .flatMapToInt(String::chars)  //Stream&lt;IntStream&gt; -&gt; IntStream
        .distinct() //unique IntStream
        .forEach(c -&gt; {
            System.out.println(&quot;Found distinct char: &quot; + ((char) c));
        });

答案2

得分: 1

这里有一种相对晦涩的方法来实现这个操作。

  • 创建一个从 0字符串长度 的流。
  • 现在查找从 i+1 开始的字符串是否有任何字符从 i 开始,如果有,那么它必定是一个重复字符。
  • 所以在流中传递索引。
  • 然后将该索引映射到实际字符。
  • 并通过删除所有重复字符来缩减原始字符串的副本。
String str = "aabcddeffg";

String result = IntStream.range(0, str.length())
    .filter(i -> str.substring(i + 1)
            .indexOf(str.charAt(i)) >= 0)
    .mapToObj(i -> str.substring(i, i + 1))
    .reduce(str, (a, b) -> a.replace(b, ""));

System.out.println(result);

输出结果

bceg
英文:

Where here is one rather obscure way of doing it.

  • Create a stream of 0 to string length.
  • now find if the string starting at i+1 has any character starting at i, if so it must be a duplicate.
  • So pass the index in the stream.
  • then map that index to the actual character.
  • and reduce a copy of the original string by removing all the duplicates.

String str = &quot;aabcddeffg&quot;;

String result = IntStream.range(0,str.length())
.filter(i-&gt;str.substring(i+1)
		.indexOf(str.charAt(i)) &gt;= 0 )
.mapToObj(i-&gt;str.substring(i,i+1)).reduce(str, (a,b)-&gt;a.replace(b,&quot;&quot;));

System.out.println(result);

Prints

bceg

</details>



huangapple
  • 本文由 发表于 2020年10月27日 08:20:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/64546709.html
匿名

发表评论

匿名网友

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

确定