【发布时间】:2014-08-17 16:20:50
【问题描述】:
我一直在研究Collections.sort 和list.sort 之间的区别,特别是关于使用Comparator 静态方法以及lambda 表达式中是否需要参数类型。在我们开始之前,我知道我可以使用方法引用,例如Song::getTitle 来克服我的问题,但是我在这里的查询并不是我想要修复的东西,而是我想要回答的东西,即为什么 Java 编译器以这种方式处理它。
这些是我的发现。假设我们有一个Song 类型的ArrayList,加上一些歌曲,有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") );
这是对两种类型的排序方法的调用,没问题:
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle()));
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle()));
一旦我开始链接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())
);
即语法错误,因为它不再知道 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())
);
现在是令人困惑的部分。对于 playlist1.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())
);
同样的事情发生在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())
);
即从比较方法调用的第一个 lambda 参数中删除类型 Song,它在比较调用和对 thenComparing 的第一次调用下显示语法错误,但对 thenComparing 的最终调用不显示语法错误 - 几乎与什么相反上面发生了!而对于所有其他 3 个示例,即 Objects.compare、List.sort 和 Collections.sort,当我删除第一个 Song 参数类型时,它会显示所有调用的语法错误。
非常感谢。
已编辑以包含我在 Eclipse Kepler SR2 中收到的错误截图,我现在发现它是 Eclipse 特定的,因为在命令行上使用 JDK8 java 编译器编译时,它可以编译。
【问题讨论】:
-
如果您在问题中包含您在所有测试中收到的所有编译错误消息,将会很有帮助。
-
老实说,我认为通过自己运行源代码来了解问题所在最容易。
-
t1和t2示例中的t2的类型是什么?我试图推断它们,但是将我的类型推断分层到编译器的类型推断上是难以处理的。 :-) -
另外你用的是什么编译器?
-
这里有两个不同的问题。其中一位回答者指出您可以使用方法引用,但您对此略过不屑一顾。正如 lambda 有“显式类型”和“隐式类型”两种风格一样,方法引用也有“精确”(一个重载)和“不精确”(多个重载)两种风格。如果不存在,则可以使用精确方法 ref 或显式 lambda 来提供额外的类型信息。 (也可以使用显式类型的证人和演员表,但通常是更大的锤子。)
标签: java sorting lambda java-8 comparator