、Map、List三者对比
(2)无序容器(就是无法保证每个元素的存储顺序,TreeSet通过Comparator或者Comparable维护一个排序顺序)
(3)只允许一个null元素
(4)Set常用实现类:HastSet、LinkedHashSet、TreeSet
- Map
- Map不是collection的子接口或实现类。Map是一个接口。
- Map的每个Entry都持有两个对象,也就是一个key一个value,Map可能会持有相同的值对象蛋健必须唯一。
- TreeMap通过Comparator或者Comparable维护了一个排序顺序。
- Map可以拥有多个null值,但只能有一个null键。
- Map常见实现类:HashMap、LinkedHashMap、Hashtable、TreeMap
- List
(1)List是一个接口,继承Collection
(2)可以拥有重复对象
(3)可以插入多个null元素
(4)是一个有序容器
(5)常用实现类:ArrayList、LinkedList、Vector(ArrayList适用于查找,LinkedList适用于增删多的操作场合。)
- 如果经常使用索引,那么应该使用List。细化:ArrayList适用于查询多的场景,LinkedList适用于增删多的场景。
- 如果你想容器中的元素能按他们插入次序进行有序存储,那么依然选择List,因为list是有序容器,他按照插入顺序进行存储。
- 如果你想保证元素的唯一性(就是没有重复元素)可以选择Set的实现类,所有Set实现类都遵循统一约束。
与LinkedList区别
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构
- 对于随机访问get和set,ArrayList性能优于LinkedList,因为LinkedList要移动指针。
- 对于新增(add)和删除(remove),LinkedList性能优于ArrayList,因为ArrayList要移动数组。
底层实现:当程序以get(index)获取集合中指定元素时,ArrayList性能大于LinkedList。因为ArrayList底层基于数组来保存集合元素,调用get时底层调用elementData[index]返回该元素,而LinkedList必须一个一个的搜索找到元素。
当程序以add()添加元素时,ArrayList会对数组元素进行整体搬家,如果添加元素导致超过底层数组长度,ArrayList必须在创建一个原来长度1.5倍的数组,再有垃圾回收器回收原来的数组,系统开销比较大。对于LinkedList主要开销再entry(index)上,他必须一个一个搜索过去,找到index处元素并在该元素之前插入新元素。即使如此LinkedList性能依然高于ArrayList。
当程序remove(index)时,ArrayList仍然需要对数组进行整体搬家,但无需创建新数组(因此ArrayList执行remove略快于add),而LinkedList使用remove时与add时开销几乎完全相同。
和ArrayList区别
- Vector的方法都是同步的(Synchronized),是线程安全的,而ArrayList不是,由于线程同步必定会损耗性能,因此ArrayList比Vector性能好(而且Java提供了一个Collections工具类,该工具类通过synchronizedList方法既可以将ArrayList包装成线程安全的)。
- Vector或者ArrayList超过原始大小时,ArrayList总是扩充为原来的1.5倍,但Vector先判断capacityIncrement变量大于0时,则扩充后的容量为原来容量+该变量值,否则扩充为原来的2倍
与HashMap区别
- HashSet实现Set接口,HashMap实现了Map接口
- HastSet仅存一个对象,HashMap存储键值对
- HashSet使用add()添加元素,hashMap通过map()添加元素
- HashSet通过对象来计算hashCode的值,对于两个对象hashCode可能相同,所以用equals来判断对象相等性。HashMap通过Key计算HashCode。
- HashMap相对于HashSet快,因为它使用唯一的键获取对象
与HashTable区别
- HashMap是非线程安全的,HashTable是线程安全的。
- HashMap允许key和value为null,而HasnTable都不能为null
- HashTable继承Dictionary虚拟类,HashMap是JDK1.2中引进的Map接口实现。
- 两者扩容方法不同,HashTable默认数组为11,扩容为old*2+1,HashMap默认大小为16,而且一定为2的指数,增加为原来的二倍。
- 两者的hash值散列到hash表算法不同,hashTable是除留余数法,直接使用Object的hashCode,而HashMap蔷薇容量为2的幂,重新根据hashCode计算hash值,在使用hash和(hash表长度-1)进行与运算,更加高效,取得的位置更加分散。
- hashMap的迭代器是fail-fast,而Hash Table的迭代器不是fail-fast,所以当有其他线程改变了HashMap的结构(增加或删除了元素)将会抛出ConcurrentModificationException,但迭代器本身移除元素不会抛出异常。
与TreeSet区别
- TreeSet是二叉树实现,TreeSet中数据是自动排好序的
- HashSet是哈希表实现,HashSet中数据是无序的,可以放入null,但只能有一个null,两者中都不能有重复。
- HashSet要求放入的对象必须实现HashCode对象,是以HashCode为表示,而具有相同内容的对象hashCode是相同的,所以放入对象内容要不同。
与TreeMap区别
- 相同点:(1)两者都是非同步集合,他们不能在多线程之间共,都能通过Collections.synchronizedMap()来实现同步
(2)运行速度都不Hash集合慢,他们内部对元素操作时间复杂度为:O(logN),而HashMap/HashSet为O(1).
(3)都是有序集合,他们内部都是排好序的
2.不同点:(1)TreeSet实现了set接口,TreeMap实现了Map接口
(2)TreeSet只存储一个对象,Tree Map存储key和Value
(3)TreeMap底层是用红黑树实现,完成数据有序插入、排序
与LinkedHashSet
- LinkedHashMap是HashMap的一个子类,存元素是都是根据hashCode来存,都是无序的,但LinkedHashMap通过next指针以链表的形式将其来连接起来,通过双向链表维护元素的次序,当遍历集合时LinkedHashMap会根据元素添加的顺序访问集合元素。(存的时候使用哈希值,因为速度快,访问的时候用链表访问,因为有序)
- LinkedHashSet是HashSet的一个子类,HashSet底层实现基于HashMap,原理同上一样。
为什么是线程不安全的?
在多线程环境下假设有容器map,已经达到需要扩容的阈值(16*0.75=12)而线程A和线程B同时对map进行插入操作,此时两者都需要扩容,此时A、B都进行了扩容,此时便产生了两个新的table,在进行重新赋值给原来的table时便会出现其中一个会覆盖另一个情况,导致其中一个线程插入操作失败。
如何解决:1.将HashMap替换成HashTable实现多线程安全,但会降低性能。
2.使用Collection类的synchronizedMap包装一下HashMap
3.使用ConCurrentHashMap,它使用分段锁来保证线程安全,效率较高。
Hash碰撞
计算得到的Hash值相同,需要放到同一个桶中,但该位置已经有值存在,即产生了冲突
解决Hash碰撞方法:1.链表法:就是将相同的hash值的对象组成一个链表放在对应hash值的位置。