按第一个字符对单词进行分组
grouping words by first character
我有: 逐行读取的文本文件。每个字符串包含一行。
我想要的: 使用 Java 流按第一个字符对所有单词进行分组。
我目前有:
public static Map<Character, List<String>> groupByFirstChar(String fileName)
throws IOException {
return Files.lines(Paths.get(PATH)).
flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
map(s -> s.toLowerCase()).
sorted((s1, s2) -> s1.compareTo(s2)).
collect(Collectors.groupingBy(s -> s.charAt(0)));
}
问题:我得到一个异常
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:646)
at textana.TextAnalysisFns.lambda(TextAnalysisFns.java:110)
at textana.TextAnalysisFns$$Lambda/159413332.apply(Unknown Source)
at java.util.stream.Collectors.lambda$groupingBy6(Collectors.java:907)
at java.util.stream.Collectors$$Lambda/189568618.accept(Unknown Source)
at java.util.stream.ReduceOpsReducingSink.accept(ReduceOps.java:169)
at java.util.stream.SortedOps$RefSortingSink$$Lambda/186370029.accept(Unknown Source)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:390)
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at textana.TextAnalysisFns.groupByFirstChar(TextAnalysisFns.java:110)
at textana.SampleTextAnalysisApp.main(SampleTextAnalysisApp.java:95)
问题:为什么我会得到 StringIndexOutOfBoundException?
根据评论中的提示解决:
public static Map<Character, List<String>> groupByFirstChar(String fileName)
throws IOException {
return Files.lines(Paths.get(PATH)).
flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
filter(s -> s.length() > 0).
map(s -> s.toLowerCase()).
collect(Collectors.groupingBy(s -> s.charAt(0)));
}
用户 Eran 的解决方案一开始会给我空字符串,这是我不想要的。
您的文件末尾很可能有一个空行,可能是由您的文本编辑器悄悄添加的,这使得最后一个 s.charAt(0)
失败。
关于如何检测它的提示:在堆栈跟踪中,阅读 collect
和 lambda
。
s.charAt(0)
在执行这条指令之前检查s是否为null以避免异常。
尝试过滤空字符串 ""
,因为它们没有导致 charAt(0)
抛出此异常的第一个字符。
您可以使用
flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
filter(s -> !s.trim().isEmpty()). //add this line
顺便说一句,您的方法可能应该使用其 fileName
参数。所以也许可以考虑将 Paths.get(PATH)
改成更像
的东西
Paths.get(fileName).
或
Paths.get(PATH).resolve(fileName)
正如评论中已经提到的那样,因为您没有更改默认比较顺序,所以您不需要显式编写
sorted((s1, s2) -> s1.compareTo(s2))
但简单
sorted()
同样适用,因为此处将应用默认顺序。
如 @Alexis C. 所述,groupBy 将 return HashMap
这意味着您的密钥将不会被排序。如果你还想保留他们的顺序,你可以使用 groupBy 和 LinkedHashMap
like
.collect(Collectors.groupingBy(s -> s.charAt(0), LinkedHashMap::new, Collectors.toList()));
我有: 逐行读取的文本文件。每个字符串包含一行。
我想要的: 使用 Java 流按第一个字符对所有单词进行分组。
我目前有:
public static Map<Character, List<String>> groupByFirstChar(String fileName)
throws IOException {
return Files.lines(Paths.get(PATH)).
flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
map(s -> s.toLowerCase()).
sorted((s1, s2) -> s1.compareTo(s2)).
collect(Collectors.groupingBy(s -> s.charAt(0)));
}
问题:我得到一个异常
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:646)
at textana.TextAnalysisFns.lambda(TextAnalysisFns.java:110)
at textana.TextAnalysisFns$$Lambda/159413332.apply(Unknown Source)
at java.util.stream.Collectors.lambda$groupingBy6(Collectors.java:907)
at java.util.stream.Collectors$$Lambda/189568618.accept(Unknown Source)
at java.util.stream.ReduceOpsReducingSink.accept(ReduceOps.java:169)
at java.util.stream.SortedOps$RefSortingSink$$Lambda/186370029.accept(Unknown Source)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:390)
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at textana.TextAnalysisFns.groupByFirstChar(TextAnalysisFns.java:110)
at textana.SampleTextAnalysisApp.main(SampleTextAnalysisApp.java:95)
问题:为什么我会得到 StringIndexOutOfBoundException?
根据评论中的提示解决:
public static Map<Character, List<String>> groupByFirstChar(String fileName)
throws IOException {
return Files.lines(Paths.get(PATH)).
flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
filter(s -> s.length() > 0).
map(s -> s.toLowerCase()).
collect(Collectors.groupingBy(s -> s.charAt(0)));
}
用户 Eran 的解决方案一开始会给我空字符串,这是我不想要的。
您的文件末尾很可能有一个空行,可能是由您的文本编辑器悄悄添加的,这使得最后一个 s.charAt(0)
失败。
关于如何检测它的提示:在堆栈跟踪中,阅读 collect
和 lambda
。
s.charAt(0)
在执行这条指令之前检查s是否为null以避免异常。
尝试过滤空字符串 ""
,因为它们没有导致 charAt(0)
抛出此异常的第一个字符。
您可以使用
flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
filter(s -> !s.trim().isEmpty()). //add this line
顺便说一句,您的方法可能应该使用其 fileName
参数。所以也许可以考虑将 Paths.get(PATH)
改成更像
Paths.get(fileName).
或
Paths.get(PATH).resolve(fileName)
正如评论中已经提到的那样,因为您没有更改默认比较顺序,所以您不需要显式编写
sorted((s1, s2) -> s1.compareTo(s2))
但简单
sorted()
同样适用,因为此处将应用默认顺序。
如 @Alexis C. 所述,groupBy 将 return HashMap
这意味着您的密钥将不会被排序。如果你还想保留他们的顺序,你可以使用 groupBy 和 LinkedHashMap
like
.collect(Collectors.groupingBy(s -> s.charAt(0), LinkedHashMap::new, Collectors.toList()));