Tra*_*ity 74 java sorting lambda comparator java-8
我一直在研究Collections.sort和之间的区别list.sort,特别是关于使用Comparator静态方法以及lambda表达式中是否需要param类型.在我们开始之前,我知道我可以使用方法引用,例如Song::getTitle克服我的问题,但我的查询并不是我想修复的东西,而是我想要的答案,即为什么Java编译器以这种方式处理它.
这是我的发现.假设我们有一个ArrayList类型Song,添加了一些歌曲,有3种标准的get方法:
ArrayList<Song> playlist1 = new ArrayList<Song>();
//add some new Song objects
playlist.addSong( new Song("Only Girl (In The World)", 235, "Rhianna") );
playlist.addSong( new Song("Thinking of Me", 206, "Olly Murs") );
playlist.addSong( new Song("Raise Your Glass", 202,"P!nk") );
Run Code Online (Sandbox Code Playgroud)
这里调用两种类型的排序方法,没问题:
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle()));
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle()));
Run Code Online (Sandbox Code Playgroud)
一旦我开始连锁thenComparing,就会发生以下情况:
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Run Code Online (Sandbox Code Playgroud)
即语法错误,因为它不再知道类型p1.所以为了解决这个问题,我将类型添加Song到第一个参数(比较):
Collections.sort(playlist1,
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
playlist1.sort(
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Run Code Online (Sandbox Code Playgroud)
现在来到ConfUSING部分.对于p laylist1.sort,即List,这解决了以下thenComparing调用的所有编译错误.但是,因为Collections.sort它解决了第一个问题,而不是最后一个问题.我测试了添加了几个额外的调用thenComparing,它总是显示最后一个错误,除非我(Song p1)输入参数.
现在我继续进行测试,创建一个TreeSet并使用Objects.compare:
int x = Objects.compare(t1, t2,
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Set<Song> set = new TreeSet<Song>(
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Run Code Online (Sandbox Code Playgroud)
同样的事情发生在,因为TreeSet,没有编译错误,但Objects.compare最后一次调用thenComparing显示错误.
任何人都可以解释为什么会发生这种情况,以及为什么(Song p1)在简单地调用比较方法时没有必要使用它(没有进一步的thenComparing调用).
关于同一主题的另一个问题是当我这样做时TreeSet:
Set<Song> set = new TreeSet<Song>(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Run Code Online (Sandbox Code Playgroud)
即Song从比较方法调用的第一个lambda参数中删除类型,它显示在比较调用和第一次调用thenComparing但不是最终调用的语法错误thenComparing- 几乎与上面发生的相反!然而,所有其他3个例子,即有Objects.compare,List.sort而且Collections.sort当我删除第一个Song参数类型它显示了所有调用的语法错误.
提前谢谢了.
编辑包括我在Eclipse Kepler SR2中收到的错误的屏幕截图,我现在发现这些错误是特定于Eclipse的,因为当在命令行上使用JDK8 java编译器进行编译时,它编译正常.

Bri*_*etz 87
首先,您说的所有示例都会导致错误,使用参考实现(JDK 8中的javac)编译良好.它们在IntelliJ中也可以正常工作,因此您看到的错误很可能是特定于Eclipse的.
您的基本问题似乎是:"当我开始链接时,为什么它会停止工作." 原因是,虽然lambda表达式和泛型方法调用是多重表达式(它们的类型是上下文相关的),当它们作为方法参数出现时,当它们显示为方法接收器表达式时,它们不是.
当你说
Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));
Run Code Online (Sandbox Code Playgroud)
有足够的类型信息来解决类型参数comparing()和参数类型p1.该comparing()调用从签名中获取其目标类型Collections.sort,因此它comparing()必须返回一个Comparator<Song>,因此p1必须是Song.
但是当你开始链接时:
Collections.sort(playlist1,
comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist()));
Run Code Online (Sandbox Code Playgroud)
现在我们遇到了问题.我们知道复合表达式comparing(...).thenComparing(...)的目标类型是Comparator<Song>,但因为链的接收器表达式comparing(p -> p.getTitle())是一个泛型方法调用,而我们无法从其他参数中推断出它的类型参数,所以我们运气不好.由于我们不知道这个表达式的类型,我们不知道它有一个thenComparing方法等.
有几种方法可以解决这个问题,所有这些方法都涉及注入更多类型信息,以便可以正确键入链中的初始对象.在这里,它们按照降低需求和增加侵入性的粗略顺序:
Song::getTitle.然后,它会提供足够的类型信息来推断comparing()调用的类型变量,从而为其提供一个类型,从而继续沿着链继续.comparing()呼叫提供类型见证:Comparator.<Song, String>comparing(...). Comparator<Song>. dka*_*zel 21
问题是类型推断.在不添加(Song s)第一个比较的情况下,comparator.comparing不知道输入的类型,因此它默认为Object.
您可以通过以下3种方法解决此问题:
使用新的Java 8方法参考语法
Collections.sort(playlist,
Comparator.comparing(Song::getTitle)
.thenComparing(Song::getDuration)
.thenComparing(Song::getArtist)
);
Run Code Online (Sandbox Code Playgroud)将每个比较步骤拉出到本地参考中
Comparator<Song> byName = (s1, s2) -> s1.getArtist().compareTo(s2.getArtist());
Comparator<Song> byDuration = (s1, s2) -> Integer.compare(s1.getDuration(), s2.getDuration());
Collections.sort(playlist,
byName
.thenComparing(byDuration)
);
Run Code Online (Sandbox Code Playgroud)
编辑
强制比较器返回的类型(注意你需要输入类型和比较键类型)
sort(
Comparator.<Song, String>comparing((s) -> s.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Run Code Online (Sandbox Code Playgroud)我认为"最后" thenComparing语法错误会误导你.它实际上是整个链的类型问题,只是编译器只将链的末尾标记为语法错误,因为当最终返回类型与我猜不匹配时.
我不确定为什么List做一个更好的推理工作,Collection因为它应该做相同的捕获类型,但显然不是.
| 归档时间: |
|
| 查看次数: |
28223 次 |
| 最近记录: |