在Java7和Java8中从现有列表创建不同的列表?
如果我有:
List<Integer> listInts = { 1, 1, 3, 77, 2, 19, 77, 123, 14, 123... }
在Java中,创建只包含来自listInts
的不同值的List<Integer> listDistinctInts
的有效方法是什么
我的直接想法是创建一个包含来自listInts
的所有值的Set<Integer> setInts
,然后调用List<Integer> listDistinctInts = new ArrayList<>(setInts);
但这似乎有潜在的低效性——是否有更好的使用Java7的解决方案
我没有使用Java 8,但我相信使用它我可以做类似的事情(?):
List<Integer> listDistinctInts = listInts.stream().distinct().collect(Collectors.toList());
这会比上述方法更有效吗?或者在Java8中有没有更有效的方法
最后,(我知道问多个问题可能会令人不快,但这是直接相关的)如果我只关心listInts
中不同元素的计数,有没有更有效的方法来获取该值(在Java 7和8中)-而不必先创建所有不同元素的列表或集合
# 1 楼答案
你应该试试
new LinkedList(new HashSet(listInts))
# 2 楼答案
Guava可以是您的选择:
API经过了极大的优化
它比
listInts.stream().distinct()
和new LinkedHashSet<>(listInts)
快# 3 楼答案
如果您正在使用Eclipse Collections(以前是GS Collections),那么可以使用方法
distinct()
使用
distinct()
而不是转换为集合然后返回列表的优点是distinct()
保留原始列表的顺序,保留每个元素的第一次出现。它通过使用集合和列表来实现如果无法将原始列表转换为GS集合类型,则可以使用ListAdapter获得相同的API
无法避免创建集合。尽管如此,UnifiedSet比HashSet更高效,因此会有一些速度优势
如果您只需要不同项目的数量,只创建一个集合而不创建列表会更有效
EclipseCollections8.0需要Java8。EclipseCollections7。x可以很好地使用Java8,但只需要Java5
注意:我是Eclipse集合的提交者
# 4 楼答案
将值添加到
listInts
检查时:如果您有一个现有列表,请使用for each语句将该列表中的所有值复制到一个新列表中,以使其“不同”:
# 5 楼答案
现在,我已经从提供的优秀答案中对大多数建议选项进行了微基准标记。与大多数与性能相关的非平凡问题一样,哪一个问题最好的答案是“这取决于”
我所有的测试都是用JMH Java Microbenchmarking Harness执行的
大多数测试都是使用JDK1.8执行的,尽管我也使用JDK1.7执行了一些测试,只是为了确保其性能没有太大差异(几乎相同)。我从目前提供的答案中测试了以下技巧:
1。Java 8流-如果使用Java 8,我已经提出了使用
stream()
的解决方案:优点现代Java 8方法,无第三方依赖关系
cons需要Java 8
2。添加到列表-由Victor2748提出的解决方案,其中当且仅当列表尚未包含该值时,构造并添加一个新列表。请注意,我还以原始列表的大小(最大可能)预先分配目标列表,以防止任何重新分配:
pros适用于任何Java版本,无需创建集然后进行复制,无需第三方DEP
cons在构建列表时,需要反复检查列表中的现有值
3。GS Collections Fast(现在是Eclipse Collections)-由Craig P. Motlin使用GS Collections library及其自定义列表类型
FastList
提出的解决方案:pros据报道,非常快速、简单的表达代码,适用于Java7和Java8
cons需要第三方库和
FastList
而不是常规的List<Integer>
4。GS Collections Adapted-FastList解决方案没有进行类似的比较,因为它需要一个
FastList
传递给方法,而不是一个好的ol'ArrayList<Integer>
,所以我还测试了Craig提出的适配器方法:pros不需要
FastList
,在Java7和Java8中工作cons必须调整列表,因此可能无法正常运行,需要第三方库
5。番石榴免疫表集——由Louis Wasserman在评论中提出的方法,以及卢声远 Shengyuan Lu在回答中使用Guava提出的方法:
pros据说速度非常快,可以在Java 7或8中运行
cons返回一个
Immutable List
,无法处理输入列表中的空值,需要第三方库7。HashSet-我的原始想法(也由EverV0id、ulix和Radiodef推荐)
pros适用于Java 7和8,无第三方依赖关系
cons不保留列表的原始顺序,必须构造集合然后复制到列表
6。LinkedHashSet-由于
HashSet
解决方案没有保留原始列表中整数的顺序,我还测试了一个使用LinkedHashSet保留顺序的版本:pros保留原始顺序,使用Java 7和Java 8,无第三方依赖关系
cons不可能像常规
HashSet
方法那样快结果
以下是我对不同大小的
listInts
的结果(结果从最慢到最快排列):1。从0-50000之间的100000个随机整数的ArrayList中选择不同的整数(即大列表,一些重复项)
2。从0-50之间的1000个随机整数的数组列表中选择不同的整数(即中等列表,许多重复项)
3。从0-100之间的100个随机整数的ArrayList中选择不同的整数(即小列表,一些重复项)
4。取不同的f0-50之间的10个随机整数的rom数组列表(即小列表,少量重复)
结论
如果您只从列表中提取一次不同的项,并且列表并不长,那么这些方法中的任何一种都应该足够了
最有效的通用方法来自第三方图书馆:GS Collections和Guava表现出色
仅当值不在新列表中时才添加到新列表的天真方法对于小列表非常有效,但一旦输入列表中有多个值,它就会执行尝试过的最差方法
番石榴{}方法在大多数情况下最快。但是请注意这些限制:返回的列表是
Immutable
,输入列表不能包含空值HashSet
方法执行非第三方方法中最好的方法,通常比Java8流更好,但是重新排序整数(这可能是一个问题,也可能不是,取决于您的用例)LinkedHashSet
方法保持了顺序,但毫不奇怪,它通常比HashSet方法更糟糕当使用具有复杂哈希代码计算的数据类型列表时,
HashSet
和LinkedHashSet
方法的性能都会更差,因此,如果您试图从List<Foo>
中选择不同的Foo
,请执行您自己的评测如果已经将GS Collections作为依赖项,那么它的性能非常好,并且比ImmutableList Guava方法更灵活。如果您没有将其作为依赖项,那么如果选择不同项的性能对应用程序的性能至关重要,则值得考虑添加它
令人失望的是,Java8流的性能似乎相当差。与我使用的方法相比,可能有更好的方法来编码
distinct()
调用,因此,当然欢迎评论或其他答案NB。我不是微基准营销专家,因此如果有人发现我的结果或方法中存在缺陷,请通知我,我将努力纠正答案
# 6 楼答案
别担心。使用哈希集是消除重复项的一种非常简单有效的方法:
在一个更具体的场景中,只是为了防止可能值的范围已知,它不是很大,而
listInts
非常大我能想到的计算列表中唯一条目数量的最有效方法是: