选择Java Collection实现的经验法则?

时间:2008-09-07 13:46:15

标签: java collections heuristics

任何人都有一个很好的经验法则,可以在列表,地图或集合等Java集合接口的不同实现之间进行选择吗?

例如,一般为什么或在什么情况下我更喜欢使用Vector或ArrayList,Hashtable或HashMap?

11 个答案:

答案 0 :(得分:81)

我真的很喜欢Sergiy Kovalchuk blog entry的这张备忘单:

Java Map/Collection Cheat Sheet

更详细的是Alexander Zagniotov的流程图,但遗憾的是它已脱机。

答案 1 :(得分:24)

我假设您从上面的答案中了解了List,Set和Map之间的区别。为什么你会在他们的实现类之间做出选择是另一回事。例如:

<强>列表

  1. ArrayList 检索速度很快,但插入速度很慢。对于读取很多但不插入/删除很多的实现来说这很好。它将数据保存在一个连续的内存块中,因此每次需要扩展时,它都会复制整个数组。
  2. LinkedList 检索速度很慢,但插入速度很快。对于插入/删除很多但不会读很多的实现来说这很好。它不会将整个数组保存在一个连续的内存块中。
  3. 设置:

    1. HashSet 不保证迭代的顺序,因此是最快的集合。它具有很高的开销并且比ArrayList慢,因此当散列速度成为一个因素时,除了大​​量数据外,你不应该使用它。
    2. TreeSet 保持数据有序,因此比HashSet慢。
    3. Map: HashMap和TreeMap的性能和行为与Set实现并行。

      不应使用Vector和Hashtable。它们是在新的Collection层次结构发布之前的同步实现,因此很慢。如果需要同步,请使用Collections.synchronizedCollection()。

答案 2 :(得分:17)

我总是根据具体用例做出这些决定,例如:

  • 我是否需要保留订单?
  • 我是否有空键/值? DUPS?
  • 是否可以被多个线程访问
  • 我需要一个键/值对
  • 我是否需要随机访问?

然后我打破了我方便的第5版 Java in a Nutshell 并比较了大约20个左右的选项。它在第五章中有很好的小表,以帮助人们弄清楚什么是合适的。

好吧,也许如果我知道一个简单的ArrayList或HashSet可以解决这个问题,我就不会把它全部看完了。 ;)但如果有关于我的使用的东西很复杂,你打赌我在书中。顺便说一句,我虽然Vector应该是'老帽' - 我已经多年没用了。

答案 3 :(得分:12)

理论上有一些有用的Big-Oh权衡,但在实践中这几乎无关紧要。

在实际基准测试中,ArrayList即使使用大型列表也可以执行LinkedList,并执行“前端附近有大量插入”等操作。学术界忽略了这样一个事实,即真实算法具有可以压倒渐近曲线的常数因子。例如,链接列表需要为每个节点分配额外的对象,这意味着创建节点的速度会更慢,而且内存访问特性会更差。

我的规则是:

  1. 始终从ArrayList和HashSet以及HashMap开始(即不是LinkedList或TreeMap)。
  2. 类型声明应始终是一个接口(即List,Set,Map),因此如果探查器或代码审查证明您可以更改实现而不会破坏任何内容。

答案 4 :(得分:8)

关于你的第一个问题......

列表,地图和集合用于不同的目的。我建议在http://java.sun.com/docs/books/tutorial/collections/interfaces/index.html阅读有关Java Collections Framework的文章。

更具体一点:

  • 如果您需要类似数组的数据结构并且需要迭代元素
  • ,请使用List
  • 如果您需要字典
  • ,请使用Map
  • 如果您只需要决定是否属于该集合,请使用Set。

关于你的第二个问题......

Vector和ArrayList的主要区别在于前者是同步的,后者是不同步的。您可以在Java Concurrency in Practice中了解有关同步的更多信息。

Hashtable(注意T不是大写字母)和HashMap之间的区别是类似的,前者是同步的,后者是不同步的。

我会说,不喜欢一种或另一种实施方式,这实际上取决于您的需求。

答案 5 :(得分:5)

对于非排序的最佳选择,十分之九以上,将是:ArrayList,HashMap,HashSet。

Vector和Hashtable是同步的,因此可能会慢一些。您很少需要同步实现,并且当您执行它们的接口时,它们的丰富程度不足以使其同步变得有用。对于Map,ConcurrentMap添加了额外的操作以使接口有用。 ConcurrentHashMap是ConcurrentMap的一个很好的实现。

LinkedList几乎不是一个好主意。即使您正在进行大量插入和删除操作,如果您使用索引来指示位置,那么需要遍历列表以查找正确的节点。 ArrayList几乎总是更快。

对于Map和Set,哈希变体将比树/排序更快。 Hash algortihms往往具有O(1)性能,而树将为O(log n)。

答案 6 :(得分:2)

列表允许重复项目,而集合仅允许一个实例。

每当我需要执行查找时,我都会使用Map。

对于特定的实现,有地图和集的顺序保留变体,但很大程度上取决于速度。对于相当小的集合,我倾向于使用ArrayList来获得相当小的Lists和HashSet,但是有很多实现(包括你自己编写的任何实现)。 HashMap在地图中很常见。任何超过“相当小”的东西,你必须开始担心记忆,以便在算法上更加具体。

This page很多的动画图片以及测试LinkedList与ArrayList的示例代码,如果您对硬编号感兴趣的话。

编辑:我希望以下链接演示这些内容实际上只是工具箱中的项目,您只需考虑您的需求:请参阅{{3}的Commons-Collections版本},MapList

答案 7 :(得分:2)

正如其他答案中所建议的,根据用例,有不同的场景使用正确的集合。我列出了几点,

<强>的ArrayList:

  • 大多数情况下,你只需要存储或迭代一堆东西&#34;然后迭代它们。基于索引的迭代速度更快。
  • 无论何时创建ArrayList,都会为其分配固定数量的内存,一旦超出,就会复制整个数组

<强>链表:

  • 它使用双向链表,因此插入和删除操作会很快,因为它只会添加或删除节点。
  • 检索速度很慢,因为它必须遍历节点。

<强> HashSet的:

  • 对某个项目做出其他是 - 否决定,例如&#34;项目是英语单词&#34;,&#34;是数据库中的项目?&#34; ,&#34;是此类别中的项目?&#34;等

  • 记住&#34;您已经处理过哪些项目&#34;,例如进行网络抓取时;

<强> HashMap中:

  • 如果您需要对给定的X说'#34;什么是Y&#34;?它通常用于实现内存中的缓存或索引,即键值对例如: 对于给定的用户ID,他们的缓存名称/用户对象是什么?。
  • 始终使用HashMap执行查找。

Vector和Hashtable是同步的,因此比较慢,如果需要同步,请使用Collections.synchronizedCollection()。 检查This是否已排序的集合。 希望这可以解决。

答案 8 :(得分:1)

我发现Bruce Eckel在Java中的思考非常有帮助。他很好地比较了不同的收藏品。我曾经保留他发布的图表,显示我的立方体墙上的继承heirachy作为快速参考。我建议你做的一件事是牢记线程安全。性能通常意味着不是线程安全。

答案 9 :(得分:0)

嗯,这取决于您的需求。一般准则是:

列表是一个集合,其中按插入顺序保留数据,并且每个元素都获得索引。

设置是一袋没有重复的元素(如果重新插入相同的元素,则不会添加)。数据没有顺序的概念。

地图,您可以按其键访问和写入数据元素,该键可以是任何可能的对象。

enter image description here 归因:https://stackoverflow.com/a/21974362/2811258

有关Java集合的更多信息,check out this article

答案 10 :(得分:0)

使用Map进行键值配对

要进行key-value跟踪,请使用Map实现。

例如,跟踪哪个人正在覆盖周末的哪一天。因此,我们想将DayOfWeek对象映射到Employee对象。

Map < DayOfWeek , Employee > weekendWorker = 
    Map.of( 
        DayOfWeek.SATURDAY , alice ,
        DayOfWeek.SUNDAY , bob
    )
;

选择Map实现之一时,需要考虑几个方面。其中包括:并发性,键和/或值中NULL值的容忍度,迭代键时的顺序,按引用对内容进行跟踪以及方便使用文字语法。

这是我制作的图表,显示了与Java 11捆绑在一起的十个Map实现中的每个实现的各个方面。

Table of map implementations in Java 11, comparing their features