集合框架
集合框架有自己的接口和实现,主要分为Set接口,List接口,Map接口和Queue接口。
它们有各自的特点:
- Set接口里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。
- List接口是一个有序的集合,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。
- Map接口中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
- Queue接口的工作原理是 FCFS 算法(First Come, First Serve)。
有序否TreeMap | 允许元素重复否 | ||
---|---|---|---|
Collection | 否 | 是 | |
List | 是 | 是 | |
Set | AbstractSet | 否 | 否 |
Set | HashSet | 否 | |
Set | TreeSet | 是(用二叉树排序) | 否 |
Map | AbstractMap | 否 | 使用key-value来映射和存储数据,Key必须惟一,value可以重复 |
Map | HashMap | 否 | 使用key-value来映射和存储数据,Key必须惟一,value可以重复 |
Map | TreeMap | 是(用二叉树排序) | 使用key-value来映射和存储数据,Key必须惟一,value可以重复 |
List接口
List 接口对Collection接口进行了简单的扩充,它的具体实现类常用的有ArrayList和LinkedList。
你可以将任何东西放到一个List容器中,并在需要时从中取出。
ArrayList从其命名中可以看出它是一种类似数组的形式进行存储,因此它的随机访问速度极快。
而LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。
Set接口
Set接口也是 Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。它的常用具体实现有HashSet和TreeSet类。
HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法。
而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的。
Map接口
Map接口是一种把键对象(key)和值对象(value)进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。
对于键对象来说,像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。
当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求。你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可能会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。
Map有两种比较常用的实现: HashMap和TreeMap。
HashSet
HashSet实现了Set<E>接口,同时继承了抽象类 AbstractSet<E>,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。
public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true。
HashSet底层采用的是HashMap进行实现。
Refer to https://swsmile.info/post/java-hashset/
HashMap
Refer to https:/swsmile.info/post/java-hashmap/
HashTable
Hashtable是继承自Dictionary抽象类,同时实现了Map<K,V>接口。
Hashtable同样是基于哈希表实现的,同样每个元素是一个键值对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。
HashSet和HashMap的区别
HashMap | HashSet |
---|---|
HashMap实现了Map<K,V>接口 | HashSet实现了Set<E>接口 |
HashMap储存键值对(HashMap以一组键值对(key-value)作为元素) | HashSet仅仅存储对象(HashSet以对象作为元素) |
使用put()方法将元素放入map中 | 使用add()方法将元素放入set中 |
HashMap中使用键对象来计算hashcode值 | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
HashMap比较快,因为是使用唯一的键来获取对象 | HashSet较HashMap来说比较慢 |
HashMap 和 Hashtable 的区别
HashMap 和 Hashtable 都实现了 Map<K,V> 接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性(Thread safety),null值允许,以及速度。
HashMap 几乎可以等价于 Hashtable,除了
- HashMap 是非 synchronized 的
- 并可以接受 null(HashMap 可以接受为 null 的键值(key)和值(value),而 Hashtable 则不行)
互斥访问(mutual exclusion)
- HashMap 是非 synchronized,而 Hashtable 是 synchronized。
- 这意味着 Hashtable 是线程安全的,多个线程可以共享一个 Hashtable;而如果没有正确的同步的话,多个线程是不能共享 HashMap 的。Java 5 提供了 ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
- 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
是否允许 null 值
- Hashtable中,key和value都不允许出现null值。
- 在HashMap中,null可以作为键,这样的键只有一个;但是,可以有一个或多个键所对应的值为null。当get()方法返回null值时,既可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
迭代器
另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而由于历史原因,Hashtable还使用了Enumeration的方式 。
所以,当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
哈希值的使用不同
HashTable直接使用对象的hashCode,如下:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
而HashMap重新计算hash值。
Reference
-
HashMap和HashSet的区别 - http://www.importnew.com/6931.html
-
HashMap和Hashtable的区别 - http://www.importnew.com/7010.html
-
HashSet HashTable HashMap的区别 及其Java集合介绍 - http://www.cnblogs.com/ywl925/p/3865269.html
-
HashMap、HashSet和HashTable的区别 - https://www.jianshu.com/p/db2376ec3af2