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

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

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

问题

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

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

以下是代码:

  1. public static void main(String[] args) {
  2. String str = "aabcddeffg";
  3. char[] charArray = str.toCharArray();
  4. List<String> strList = new ArrayList<>();
  5. for (int i = 0; i < charArray.length; i++) {
  6. String myChar = String.valueOf(charArray[i]);
  7. strList.add(myChar);
  8. }
  9. Map<String, Long> myMap =
  10. strList.stream().
  11. collect(
  12. Collectors.groupingBy(
  13. Function.identity(), Collectors.counting()
  14. )
  15. );
  16. myMap.forEach((k, v) -> {
  17. if (v == 1) {
  18. System.out.println(k);
  19. }
  20. });
  21. }
  22. }
英文:

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:

  1. public static void main(String[] args) {
  2. String str = &quot;aabcddeffg&quot;;
  3. char[] charArray = str.toCharArray();
  4. List&lt;String&gt; strList = new ArrayList&lt;&gt;();
  5. for(int i=0; i&lt; charArray.length; i++){
  6. String myChar = String.valueOf(charArray[i]);
  7. strList.add(myChar);
  8. }
  9. Map&lt;String, Long&gt; myMap =
  10. strList.stream().
  11. collect(
  12. Collectors.groupingBy(
  13. Function.identity(),Collectors.counting()
  14. )
  15. );
  16. myMap.forEach((k, v) -&gt; {
  17. if (v == 1) {
  18. System.out.println(k);
  19. }
  20. });
  21. }
  22. }

答案1

得分: 1

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

  1. Map<Integer, Long> charFrequency = strList.stream() //Stream<String>
  2. .flatMapToInt(String::chars) //Stream<IntStream> -> IntStream
  3. .boxed() //IntStream -> Stream<Integer>
  4. .collect(Collectors.groupingBy( //Map<K, V>
  5. Function.identity(), //K == 我们的整数
  6. Collectors.counting() //V == 它们的数量
  7. ));
  8. charFrequency.entrySet().stream() //Stream<Map.Entry<Integer, Long>>
  9. .filter(ent -> ent.getValue() == 1) //只保留值为1的条目
  10. .mapToInt(Map.Entry::getKey) //Stream<Entry> -> IntStream
  11. .forEach(c -> {
  12. System.out.println("找到唯一字符:" + ((char) c));
  13. });

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

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

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

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

嗯,总有Stream#distinct方法。

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

  1. List<Integer> distinctChars = strList.stream() //Stream<String>
  2. .flatMapToInt(String::chars) //Stream<IntStream> -> IntStream
  3. .distinct() //唯一的 IntStream
  4. .boxed() //唯一的 Stream<Integer>
  5. .collect(Collectors.toList()); //List<Integer>
  6. distinctChars.forEach(c -> {
  7. System.out.println("找到不同字符:" + ((char) (int) c));
  8. });

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

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

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

  1. Map&lt;Integer, Long&gt; charFrequency = strList.stream() //Stream&lt;String&gt;
  2. .flatMapToInt(String::chars) //Stream&lt;IntStream&gt; -&gt; IntStream
  3. .boxed() //IntStream -&gt; Stream&lt;Integer&gt;
  4. .collect(Collectors.groupingBy( //Map&lt;K, V&gt;
  5. Function.identity(), //K == our integers
  6. Collectors.counting() //V == the number of them
  7. ));
  8. charFrequency.entrySet().stream() //Stream&lt;Map.Entry&lt;Integer, Long&gt;&gt;
  9. .filter(ent -&gt; ent.getValue() == 1) //only entries with value of 1
  10. .mapToInt(Map.Entry::getKey) //Stream&lt;Entry&gt; -&gt; IntStream
  11. .forEach(c -&gt; {
  12. System.out.println(&quot;Found unique character: &quot; + ((char) c));
  13. });

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

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

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:

  1. List&lt;Integer&gt; distinctChars = strList.stream() //Stream&lt;String&gt;
  2. .flatMapToInt(String::chars) //Stream&lt;IntStream&gt; -&gt; IntStream
  3. .distinct() //unique IntStream
  4. .boxed() //unique Stream&lt;Integer&gt;
  5. .collect(Collectors.toList()); //List&lt;Integer&gt;
  6. distinctChars.forEach(c -&gt; {
  7. System.out.println(&quot;Found distinct char: &quot; + ((char) (int) c));
  8. });

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

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

答案2

得分: 1

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

  • 创建一个从 0字符串长度 的流。
  • 现在查找从 i+1 开始的字符串是否有任何字符从 i 开始,如果有,那么它必定是一个重复字符。
  • 所以在流中传递索引。
  • 然后将该索引映射到实际字符。
  • 并通过删除所有重复字符来缩减原始字符串的副本。
  1. String str = "aabcddeffg";
  2. String result = IntStream.range(0, str.length())
  3. .filter(i -> str.substring(i + 1)
  4. .indexOf(str.charAt(i)) >= 0)
  5. .mapToObj(i -> str.substring(i, i + 1))
  6. .reduce(str, (a, b) -> a.replace(b, ""));
  7. System.out.println(result);

输出结果

  1. 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.
  1. String str = &quot;aabcddeffg&quot;;
  2. String result = IntStream.range(0,str.length())
  3. .filter(i-&gt;str.substring(i+1)
  4. .indexOf(str.charAt(i)) &gt;= 0 )
  5. .mapToObj(i-&gt;str.substring(i,i+1)).reduce(str, (a,b)-&gt;a.replace(b,&quot;&quot;));
  6. System.out.println(result);

Prints

  1. bceg
  2. </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:

确定