一、容器的分类
Collection 接口的接口 对象的集合(单列集合)
- List //接口—元素按进入先后有序保存,可重复
- ArrayList //接口实现类, 数组, 随机访问, 没有同步, 线程不安全
- LinkedList //接口实现类, 链表, 插入删除, 没有同步, 线程不安全
- Vector //接口实现类 数组, 同步, 线程安全
- Stack //是Vector类的实现类
- Set // 接口: 仅接收一次,无序不可重复,并做内部排序
- HashSet // 使用hash表(数组)存储元素
- LinkedHashSet // 链表维护元素的插入次序
- TreeSet // 底层实现为二叉树,元素排好序
- HashSet // 使用hash表(数组)存储元素
Map 接口 键值对的集合 (双列集合)
- HashMap // 接口实现类 ,没有同步, 线程不安全-
- LinkedHashMap // 双向链表和哈希表实现
- ConcurrentHashMap // 线程安全。 1.7:使用了分段锁,1.8:使用了CAS算法。
- 分段锁:解决 HashTable 锁范围广的问题,将数据分段存储,给每一段数据加锁
- CAS算法:比较并交换算法。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
- TreeMap // 红黑树对所有的key进行排序
- Hashtable //接口实现类, 同步, 线程安全、效率低(同步时也不能访问其他方法,锁的范围太大)
二、List 和 Set详解
1、List 和 Set 的区别
1. 有序性
1. List:保证按插入顺序排序
2. Set:存储和取出顺序不一致
2. 唯一性
1. List:可以重复
2. Set:元素唯一
3. 获取元素方式
1. List:可以通过索引直接操作元素
2. Set:不能根据索引获取元素
2、List实现类
- ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
- LinkedList: 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
- Vector: 底层数据结构是数组,查询快,增删慢,线程安全(加锁),效率低,可以存储重复元素
3、Set实现类
-
HashSet: 底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
实现唯一性的流程: 1. 将储存元素用hash()算法生成一个HashCode值 2. 然后和已储存的hashCode值做比较 3. 判断两个对象hashCode和equals同时相等,对象才相等 4. 如果相等,那么就是同一个对象,重复元素会覆盖掉;如果不相等,那么就是不同的对象,就存储 5. 如果HashCode相等,就产生了哈希冲突 6. 解决办法:在该哈希值处创建一个链表,这样就保证了元素的唯一性。
初始化容量16,加载因子0.75。
-
LinkedHashSet: 底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高
-
TreeSet: 底层数据结构采用红黑树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode 和 equals,二叉树结构保证了元素的有序性
自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列
TreeSet排序方式:
1. 基本数据类型默认按升序排序
2. 自定义排序:- 自然排序:实现Comparable接口,并重写Compareto方法
- 比较器排序:重写Comparator接口中的Compare方法
List和Set总结
- List,Set都是继承自Collection接口,Map则不是
- list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值
- ArrayList与LinkedList的区别:
- Arraylist:底层是数组,在内存中是连续存储,地址连续,对数据进行经常访问的场景
- LinkedList:底层是链表,在内存中不是连续存储,地址不连续,适用于要头尾操作或插入指定位置的场景
- :HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为快速查找而设计的Set,通常都应该使用HashSet,在需要排序的功能时,使用TreeSet。
三、Map详解
1、Map概念
Map用于保存具有映射关系的数据,
Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。
所以通过指定的key就可以取出对应的value。
1. TreeMap是有序的,HashMap和HashTable是无序的。
2. Hashtable的方法是同步的,HashMap的方法不是同步的。
3、HashMap和HashTable的比较
HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
4、TreeMap
小结
-
HashMap:非线程安全,基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。
-
TreeMap:非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
应用场景
8、适用场景
HashMap和HashTable:HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允许空键值,而HashTable不允许。
在实际使用中,如果不需要保证元素的顺序,就使用HashMap,如果需要保证元素的插入顺序或者访问顺序,就使用LinkedHashMap,如果需要按照键值排序,就使用TreeMap。
HashMap:适用于Map中插入、删除和定位元素。
Treemap:适用于按自然顺序或自定义顺序遍历键(key)。
四、重点问题
1、说说List,Set,Map三者的区别?
- List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象。
- Map(用Key来搜索的专家): 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
2、Arraylist 与 LinkedList 区别?
- 1.是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
- 2.底层数据结构: Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构
- 3.插入和删除是否受元素位置的影响:时间复杂度为 O(n-i)
① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。
② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O₁。 - 4.是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。
- 5.内存空间占用: ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
尽量避免同时遍历和删除集合。因为这会改变集合的大小;