集合概述
Java中除了以Map结尾的类之外,其他类都实现了Collection接口,并且以Map结尾的类都实现了Map接口。
List,Set,Map的区别
List:存储的元素是有序的,可重复的
Set:存储的元素是无序的,不可重复的
Map:使用键值对存储,key是无序的,不可重复的;value是无序的,可重复的。每个键最多映射到一个值
集合框架底层数据结构
List
Arralist:Object[] 数组
Vector:Object[] 数组
LinkedList:双向链表(JDK1.6之前的是循环链表,JDK1.7取消了循环)
Set
HashSet:(无序,唯一)基于HashMap实现的,底层采用HashMap来保存元素
LinkedHashSet:它是HashSet的子类,内部通过LinkedHashMap来实现的。类似于LinkedHashMap内部基于HashMap实现的一样,有一定区别
TreeSet:(有序,唯一)红黑树
Map
HashMap:JDK1.8之前的HashMap由数组和链表组成的,数组是HashMap的主体,链表是主要为了解决哈希冲突而存在的。JDK1.8以后,在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认是8)【将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树】时,将链表转化为红黑树,以减少搜索时间。;
LinkedHashMap:它继承自HashMap,底层是基于拉链式散列结构,即由数组和链表或红黑树组成。另外,LinkedHashMap在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
HashTable:数组+链表组成,数组是HashMap的主体,链表是为了解决哈希冲突而存在的。
TreeMap:红黑树
如何选用集合?
根据键值获取元素值——Map接口下的集合
需要排序时选择——TreeMap
不需要排序时选择——HashMap
需要保证线程安全——ConcurrentHashMap
需要存放元素值——Collection接口下的集合
需要保证元素唯一——Set接口(TreeSet或者HashSet)
不需要保证元素唯一——List接口(ArrayList或者LinkedList)
为什么要使用集合?
需要保存一组类型相同的数据时,用一个容器来保存,这个容器是数组,但是使用数组存储对象有一定的弊端,因为在实际开发中,存储的数据的类型有很多种,集合用来存储多个数据的。
数组的缺点是声明之后长度不可变,同时声明数组时的数据类型也决定了数组存储的数据的类型。数组存储数据是有序的,可重复的,特点单一;但是集合提高了数据存储的灵活性,Java集合不仅可以用来存储不同类型不同数量的对象,还可以保存具有映射关系的数据。
Iterator 迭代器
Iterator 对象称为迭代器(设计模式的⼀种),迭代器可以对集合进⾏遍历,但每⼀个集合内部的数据结构可能是不相同的,所以每⼀个集合”存取“都很可能是不⼀样的,虽然我们可以⼈为地在每⼀个类中定义 hasNext()——集合中是否还有元素和next()——获得集合中的下一个元素,这样做会让整个集合体系过于臃肿。于是就有了迭代器。
迭代器的好处
迭代器是将这样的⽅法抽取出接⼝,然后在每个类的内部,定义⾃⼰迭代⽅式,这样做就规定了整个集合体系的遍历⽅式都是 hasNext() 和 next() ⽅法,使⽤者不⽤管怎么实现的,会⽤即可。迭代器的定义为:提供⼀种⽅法访问⼀个容器对象中各个元素,⽽⼜不需要暴露该对象的内部细节。
迭代器的用处:
Iterator 主要是⽤来遍历集合⽤的,它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出ConcurrentModificationException 异常。
举例说明迭代器的使用(遍历HashMap)
Map<Integer, String> map = new HashMap();
map.put(1, "Java");
map.put(2, "C++");
map.put(3, "PHP");
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
System.out.println(entry.getKey() + entry.getValue());
}
线程不安全的集合如何解决?
Arraylist , LinkedList , Hashmap , HashSet , TreeSet , TreeMap , PriorityQueue 都是线程不安全的
解决办法:使⽤线程安全的集合来代替
使⽤线程安全的集合的话, java.util.concurrent包中提供了很多并发容器:
- ConcurrentHashMap : 可以看作是线程安全的 HashMap
- CopyOnWriteArrayList :可以看作是线程安全的 ArrayList ,在读多写少的场合性能⾮常好,远远好于 Vector
- ConcurrentLinkedQueue :⾼效的并发队列,使⽤链表实现。可以看做⼀个线程安全的LinkedList ,这是⼀个⾮阻塞队列
- BlockingQueue : 这是⼀个接⼝,JDK 内部通过链表、数组等⽅式实现了这个接⼝。表示阻塞队列,⾮常适合⽤于作为数据共享的通道
- ConcurrentSkipListMap :跳表的实现。这是⼀个 Map ,使⽤跳表的数据结构进⾏快速查找