Java 按第一个字符对单词进行分组

Java 按第一个字符对单词进行分组,java,java-8,java-stream,Java,Java 8,Java Stream,我拥有的:一个逐行读取的文本文件。每个字符串包含一行 我想要的:使用Java Streams按第一个字符对所有单词进行分组 到目前为止我拥有的: public static Map<Character, List<String>> groupByFirstChar(String fileName) throws IOException { return Files.lines(Paths.get(PATH)). flatM

我拥有的:一个逐行读取的文本文件。每个字符串包含一行

我想要的:使用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)));
}
问题:为什么我会得到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)));
}
公共静态映射groupByFirstChar(字符串文件名)
抛出IOException{
返回Files.line(PATH.get(PATH))。
平面图(s->Stream.of(s.split(“[^a-zA-Z]”))的平面图。
过滤器(s->s.length()>0)。
映射(s->s.toLowerCase()。
collect(collector.groupingBy(s->s.charAt(0));
}

用户Eran的解决方案在开始时会给我一些我不想要的空字符串。

您很可能在文件末尾有一个空行,可能是由您的文本编辑器无声添加的,这使得最后一个
s.charAt(0)
失败

关于如何检测它的提示:在堆栈跟踪中,读取
collect
lambda$16

 s.charAt(0) 

在执行此指令之前,请检查s是否为null,以避免出现异常。

尝试筛选空字符串
,因为它们没有导致
字符(0)
引发此异常的第一个字符

你可以用

flatMap(s -> Stream.of(s.split("[^a-zA-Z]"))).
filter(s -> !s.trim().isEmpty()). //add this line
顺便说一句,您的方法可能应该使用其
文件名
参数。因此,可以考虑更改<代码>路径。将(路径)< /C> >类似于

Paths.get(fileName).

同样正如评论中已经提到的,因为您并没有更改默认的比较顺序,所以不需要显式地编写

sorted((s1, s2) -> s1.compareTo(s2))
但很简单

sorted()
也将起作用,因为默认顺序将在此处应用


正如groupBy所提到的,它将返回
HashMap
,这意味着您的密钥不会被订购。如果您还想保留他们的订单,您可以将groupBy与
LinkedHashMap
like一起使用

.collect(Collectors.groupingBy(s -> s.charAt(0), LinkedHashMap::new, Collectors.toList()));

顺便说一句,您可以跳过sorted()操作的参数,因为您使用的是自然排序。您甚至可以删除
sorted
,因为
groupingBy
在幕后使用
HashMap
,所以排序的概念无论如何都会丢失。@AlexisC。我们只会丢失键的顺序,但是如果我没有弄错的话,
列表中的值应该保持顺序,这可能是OP试图在这里实现的。@AlexisC。没问题,这是一个有趣的观察结果,这让我想到了一个解决方案,它还可以保存键的顺序。我正在处理
收集器。groupingBy(s->s.charAt(0),LinkedHashMap::new,??
但是需要学习在
部分:)
groupingBy(s->s.charAt(0),LinkedHashMap::new,toList())中放置什么,因为您希望在
列表中累积值
sorted()
.collect(Collectors.groupingBy(s -> s.charAt(0), LinkedHashMap::new, Collectors.toList()));