按第一个字符分组单词

Skl*_*ogW 4 java java-8 java-stream

我有:一行一行的文本文件.每个String包含一行.

我想要的:使用Java Streams按第一个字符对所有单词进行分组.

到目前为止我所拥有的:

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)));
}
Run Code Online (Sandbox Code Playgroud)

问题:我得到一个例外

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$16(TextAnalysisFns.java:110)
at textana.TextAnalysisFns$$Lambda$36/159413332.apply(Unknown Source)
at java.util.stream.Collectors.lambda$groupingBy$196(Collectors.java:907)
at java.util.stream.Collectors$$Lambda$23/189568618.accept(Unknown Source)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.stream.SortedOps$RefSortingSink$$Lambda$37/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)
Run Code Online (Sandbox Code Playgroud)

问题:为什么我会收到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)));
}
Run Code Online (Sandbox Code Playgroud)

用户的解决方案Eran会在一开始就给我空字符串,这是我不想要的.

Psh*_*emo 6

尝试过滤空字符串,""因为它们没有导致charAt(0)抛出此异常的第一个字符.

您可以使用

flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
filter(s -> !s.trim().isEmpty()). //add this line
Run Code Online (Sandbox Code Playgroud)

BTW你的方法应该使用它的fileName论点.所以也许可以考虑Paths.get(PATH)换成更像的东西

Paths.get(fileName).
Run Code Online (Sandbox Code Playgroud)

要么

Paths.get(PATH).resolve(fileName)
Run Code Online (Sandbox Code Playgroud)

同样如评论所述,因为您没有更改默认比较顺序,所以您不需要显式写入

sorted((s1, s2) -> s1.compareTo(s2))
Run Code Online (Sandbox Code Playgroud)

但很简单

sorted()
Run Code Online (Sandbox Code Playgroud)

也将工作,因为默认订单将在这里应用.


正如@Alexis C. groupBy所说,将返回HashMap,这意味着您的钥匙将不会被订购.如果您还想保留它们的顺序,您可以使用GROUPBY与LinkedHashMap

.collect(Collectors.groupingBy(s -> s.charAt(0), LinkedHashMap::new, Collectors.toList()));
Run Code Online (Sandbox Code Playgroud)

  • 您甚至可以删除“sorted”,因为“groupingBy”在幕后使用“HashMap”,因此排序的概念无论如何都会丢失。 (2认同)
  • @亚历克西斯C。我们只会丢失键的顺序,但如果我没有记错的话,“List&lt;String&gt;”中的值应该保持有序,这可能是OP试图在这里实现的目标。 (2认同)