集合体系整理
前言
- 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象进行操作,就需要对对象进行存储
- 数组虽然可以存储对象,但是长度上固定的
- 集合的长度是可变的
- 数组中可以存储基本数据类型
- 集合中只能存储对象
集合体系图
Iterator接口
- Iterator接口,这是一个用于遍历集合中元素的接口
- 主要包含hashNext(),next(),remove()三种方法
- 它的一个子接口LinkedIterator在它的基础上又添加了三种方法,分别是add(),previous(),hasPrevious()
- Iterator接口,那么在遍历集合中元素的时候,只能往后遍历,被遍历后的元素不会在遍历到,通常无序集合实现的都是这个接口,比如HashSet,HashMap
- 元素有序的集合,实现的一般都是LinkedIterator接口,实现这个接口的集合可以双向遍历,既可以通过next()访问下一个元素,又可以通过previous()访问前一个元素,比如ArrayList。
Collection接口
- Collection是集合类根接口,衍生出两个子类接口List和Set
- Collection定义了集合框架的共性功能
List接口
List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。
- ArrayList:线程不安全,查询速度快,元素有序,可重复
- Vector:线程安全,但速度慢,已被ArrayList替代
- LinkedList:链表结构,增删速度快
LinkedList经常用在增删操作较多而查询操作很少的情况下,ArrayList则相反
Set接口
Set里存放的对象是无序(存入和取出的顺序不一定一致),不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。
- HashSet::
- 底层数据结构是哈希表。是线程不安全的。不同步
- 通过元素的两个方法,hashCode和equals来保证唯一性
- 如果元素的HashCode值相同,才会判断equals是否为true。
- 如果元素的hashcode值不同,不会调用equals。
- 无序
- TreeSet:
- 有序
- 线程不安全,可以对Set集合中的元素进行排序
- 通过compareTo或者compare方法来保证元素的唯一性,元素以二叉树的形式存放。
Map接口
- Map提供了一种映射关系,元素是以键值对(key-value)的形式存储的,能根据key快速查找value;
- Map中的键值对以Entry类型的对象实例形式存在;
- key值不能重复,value值可以重复;
HashMap
底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
HashMap原理:
- HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
- 数组中的每一项又是一个Entry,其中包含了key和value,也就是键值对,另外还包含了一个next的Entry指针
- 因为持有下一个Entry指针,所以构成链表
- 往HashMap中put元素的时候,先根据key的hashCode重新计算hash值
- 根据hash值得到这个元素在数组中的位置(即下标)
- 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。
- 如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上
- 从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
一句话原理总结:
简单来说,HashMap在底层将key-value当作一个整体处理,这个整体就是一个Entry对象。HashMap底层采用一个Entry[]数组来保存所有的key-value,每个Entry包含了key-value,还包含了next的Entry指针,因此构成一个链表。当要存储一个Entry对象时,会根据hash算法决定在数组中的存储位置,再根据equals方法,决定其在该数组位置上的链表的存储位置;在需要取出一个Entry时,也会根据hash算法找到其在这个数组的存储位置,再根据equel方法,从该位置找到对应的Entry对象。
hashmap两种遍历方式
第一种:
1 | Map map = new HashMap(); |
第二种:
1 | Map map = new HashMap(); |
浅析ConcurrentHashMap
- public V get(Object key)不涉及到锁,也就是说获得对象时没有使用锁;
- put、remove方法要使用锁,但并不一定有锁争用,原因在于ConcurrentHashMap将缓存的变量分到多个Segment,每个Segment上有一个锁,只要多个线程访问的不是一个Segment就没有锁争用,就没有堵塞,各线程用各自的锁,ConcurrentHashMap缺省情况下生成16个Segment,也就是允许16个线程并发的更新而尽量没有锁争用;
- Hashtable对get,put,remove都使用了同步操作,也就是说如果有线程正在遍历集合,其他的线程就暂时不能使用该集合了,这样无疑就很容易对性能和吞吐量造成影响。而ConcurrentHashMap则不同,它只对put,remove操作使用了同步操作,get操作并不影响
- Hashtable在使用iterator遍历的时候,如果其他线程,包括本线程对Hashtable进行了put,remove等更新操作的话,就会抛出ConcurrentModificationException异常,但如果使用ConcurrentHashMap的话,就不用考虑这方面的问题了
浅析HashMap,HashTable,ConcurrentHashMap
- HashMap如上所诉,不同步,线程不安全,不使用用与多线程高并发情况下
- Hashtable,被遗弃的类,线程安全是因为在所有方法上都加了synchronized来实现线程安全,导致多线程访问效率低
- Synchronized Map(通过Collections.synchronizedMap()来包装一个hashmap)和hashtable区别不大,唯一区别就是没有被遗弃
- ConcurrentHashMap,默认允许16个线程读写这个map,不像Hashtable和Synchronized Map一样,没有锁整个整个map,而是划分了多个段(Segment),只会锁需要操作的那一段数据
TreeMap
底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。
集合输出(遍历)
- Iterator: 迭代输出,使用最多的输出方式
- ListIterator:是Iterator的子接口,专门用于输出List中的内容。
- foreach输出:JDK1.5之后提供的新功能,可以输出数组或集合。
- for循环
集合的工具类
Collections:集合框架的工具类。里面定义的都是静态方法。
Collections和Collection有什么区别?
Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。
它有两个常用的子接口,
——List:对元素都有定义索引。有序的。可以重复元素。
——Set:不可以重复元素。无序。
Collections是集合框架中的一个工具类。该类中的方法都是静态的。
提供的方法中有可以对list集合进行排序,二分查找等方法。
通常常用的集合都是线程不安全的。因为要提高效率。
如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
总结
List:add/remove/get/set。
1,ArrayList:其实就是数组,容量一大,频繁增删就是噩梦,适合随机查找;
2,LinkedList:增加了push/[pop|remove|pull],其实都是removeFirst;
3,Vector:历史遗留产物,同步版的ArrayList,代码和ArrayList太像;
4,Stack:继承自Vector。Java里其实没有纯粹的Stack,可以自己实现,用组合的方式,封装一下LinkedList即可;
5,Queue:本来是单独的一类,不过在SUN的JDK里就是用LinkedList来提供这个功能的,主要方法是offer/pull/peek,因此归到这里呢。
Set:add/remove。可以用迭代器或者转换成list。
1,HashSet:内部采用HashMap实现的;
2,LinkedHashSet:采用LinkedHashMap实现;
3,TreeSet:TreeMap。
Map:put/get/remove。
1,HashMap/HashTable:散列表,和ArrayList一样采用数组实现,超过初始容量会对性能有损耗;
2,LinkedHashMap:继承自HashMap,但通过重写嵌套类HashMap.Entry实现了链表结构,同样有容量的问题;
3,Properties:是继承的HashTable。
顺便说一下Arrays.asList,这个方法的实现依赖一个嵌套类,这个嵌套类也叫ArrayList!
手写集合
ArrayList
1 | /** |
- LinkedList
1 | public class MyLinkedList<AnyType> { |