英文:
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 = "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);
}
});
}
}
答案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<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 == our integers
Collectors.counting() //V == the number of them
));
charFrequency.entrySet().stream() //Stream<Map.Entry<Integer, Long>>
.filter(ent -> ent.getValue() == 1) //only entries with value of 1
.mapToInt(Map.Entry::getKey) //Stream<Entry> -> IntStream
.forEach(c -> {
System.out.println("Found unique character: " + ((char) c));
});
And for doing it for a single string, it's even easier (you save the conversions):
Map<Integer, Long> charFrequency = someString.chars() //Stream<Integer>
.collect(Collectors.groupingBy( //Map<K, V>
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<Integer> distinctChars = strList.stream() //Stream<String>
.flatMapToInt(String::chars) //Stream<IntStream> -> IntStream
.distinct() //unique IntStream
.boxed() //unique Stream<Integer>
.collect(Collectors.toList()); //List<Integer>
distinctChars.forEach(c -> {
System.out.println("Found distinct char: " + ((char) (int) c));
});
If you want to avoid collecting it, you can also avoid all the hassle around the type boxing:
strList.stream() //Stream<String>
.flatMapToInt(String::chars) //Stream<IntStream> -> IntStream
.distinct() //unique IntStream
.forEach(c -> {
System.out.println("Found distinct char: " + ((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
tostring 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 = "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);
Prints
bceg
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论