被Java8比较器类型推断弄糊涂了
我一直在研究被Java8比较器类型推断弄糊涂了,java,sorting,lambda,java-8,comparator,Java,Sorting,Lambda,Java 8,Comparator,我一直在研究Collections.sort和list.sort之间的区别,特别是关于使用Comparator静态方法以及lambda表达式中是否需要参数类型。在我们开始之前,我知道我可以使用方法引用,例如Song::getTitle来克服我的问题,但是我这里的查询不是我想要解决的问题,而是我想要一个答案,即为什么Java编译器会以这种方式处理它 这是我的发现。假设我们有一个Song类型的ArrayList,添加了一些歌曲,有3种标准的get方法: ArrayList<Song&g
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())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
i、 e.语法错误,因为它不再知道p1
的类型。为了解决这个问题,我在第一个参数(比较)中添加了类型Song
:
现在是令人困惑的部分。对于playlist1.sort
,即列表,这将解决以下调用和比较调用的所有编译错误。但是,对于Collections.sort
,它会对第一个进行求解,但不会对最后一个进行求解。我测试了添加了几个额外的调用到,然后进行比较
,最后一个调用总是显示错误,除非我将(Song p1)
作为参数
现在,我通过创建树集和使用对象进一步测试了这一点。比较:
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())
);
i、 e.从比较方法调用的第一个lambda参数中删除类型Song
,它在调用comparing和第一个调用然后比较
下显示语法错误,但在最后一个调用然后比较
下显示语法错误-几乎与上述情况相反!然而,对于所有其他3个示例,即使用对象。比较,列表。排序和集合。排序当我删除第一个歌曲
参数类型时,它会显示所有调用的语法错误
非常感谢
编辑以包含我在EclipseKeplerSR2中收到的错误的屏幕截图,我现在发现这些错误是特定于Eclipse的,因为在命令行上使用JDK8Java编译器编译时,它编译正常
问题在于类型推断。如果不在第一次比较中添加(Song s)
,comparator.Comparating
不知道输入的类型,因此默认为Object
您可以通过以下三种方式之一解决此问题:
使用新的Java8方法引用语法
Collections.sort(playlist,
Comparator.comparing(Song::getTitle)
.thenComparing(Song::getDuration)
.thenComparing(Song::getArtist)
);
将每个比较步骤拉入本地参考
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)
);
comparatorbyname=(s1,s2)->s1.getArtist().compareTo(s2.getArtist());
比较器byDuration=(s1,s2)->Integer.compare(s1.getDuration(),s2.getDuration());
收藏。排序(播放列表,
别名
.然后比较(按持续时间)
);
编辑
强制比较器返回的类型(注意,您需要输入类型和比较键类型)
排序(
Comparator.comparing((s)->s.getTitle())
.Then比较(p1->p1.getDuration())
.Then比较(p1->p1.getArtister())
);
我认为“last”然后比较
语法错误会误导您。这实际上是整个链的类型问题,只是编译器只将链的结尾标记为语法错误,因为我猜这是最终返回类型不匹配的时候
我不确定为什么列表
比收集
做得更好,因为它应该做相同的捕获类型,但显然不是。问题是类型推断。如果不在第一次比较中添加(Song s)
,comparator.Comparating
不知道输入的类型,因此默认为Object
您可以通过以下三种方式之一解决此问题:
使用新的Java8方法引用语法
Collections.sort(playlist,
Comparator.comparing(Song::getTitle)
.thenComparing(Song::getDuration)
.thenComparing(Song::getArtist)
);
将每个比较步骤拉入本地参考
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)
);
comparatorbyname=(s1,s2)->s1.getArtist().compareTo(s2.getArtist());
比较器byDuration=(s1,s2)->Integer.compare(s1.getDuration(),s2.getDuration());
收藏。排序(播放列表,
别名
.然后比较(按持续时间)
);
编辑
强制比较器返回的类型(注意,您需要输入类型和比较键类型)
排序(
Comparator.comparing((s)->s.getTitle())
.Then比较(p1->p1.getDuration())
.Then比较(p1->p1.getArtister())
);
我认为“last”然后比较
语法错误会误导您。这实际上是整个链的类型问题,只是编译器只将链的结尾标记为语法错误,因为我猜这是最终返回类型不匹配的时候
我不知道为什么List
比Collection
做得更好,因为它应该做相同的捕获类型,但显然不是。playli1。sort(…)
为类型变量E创建一个歌曲的范围,从playli1的声明开始,它会“涟漪”到比较符
在Collections.sort(…)
中,没有这样的界限,并且来自第一个比较器类型的推断不足以让编译器推断其余的比较器
我认为您可以从集合中获得“正确”的行为。sort(…)
,但不需要安装java 8来测试它。Playly1。sort(…)
为类型变量E创建一个歌曲绑定,从Playly1的声明开始,它会“涟漪”到比较器
在Collections.sort(…)
中,没有这样的界限,并且从第一个比较器的类型推断的结果对于
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)
);
sort(
Comparator.<Song, String>comparing((s) -> s.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));
Collections.sort(playlist1,
comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist()));
Comparator<Document> comparator = Comparator.comparing((Document hist) -> (String) hist.get("orderLineStatus"), reverseOrder())
.thenComparing(hist -> (Date) hist.get("promisedShipDate"))
.thenComparing(hist -> (Date) hist.get("lastShipDate"));
list = list.stream().sorted(comparator).collect(Collectors.toList());