你们都知道在JAVA中若是咱们要存储和树立一组同类型的数据的时候,咱们通常都采用数组来存储。可是你们知道数组一旦被建立,其长度就固定不变了,因此使用数组的时候须要知道或者说是咱们要估算一下数据的规模,以方便咱们建立长度适合的数组。若是咱们估计的长度比实际须要的长度大,那则会浪费存储空间;若比实际长度小,则处理数据时会遇到麻烦,所以,用数据存储数目不肯定的元素那样是一个不明智的选择。算法
ArrayList 和Vector是采起数组体式格式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,然则插入数据要设计到数组元素移动等内存操纵,因此索引数据快插入数据慢,Vector由于应用了synchronized办法(线程安然)因此机能上比ArrayList要差,LinkedList应用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,然则插入数据时只需要记录本项的先后项便可,因此插入数度较快!Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack数组
├Set
│├HashSet
│├LinkedHashSet
│└TreeSet安全
Map
├Hashtable
├HashMap
├LinkedHashMap
├LinkedHashMap
└TreeMap数据结构
Collection接口
Collection是最根蒂根基的凑集接口,一个Collection表明一组Object,即Collection的元素(Elements)。一些Collection允许雷同的元素而另外一些不可。一些能排序而另外一些不可。Java SDK不供给直接持续自Collection的类,Java SDK供给的类都是持续自Collection的“子接口”如List和Set。
全部实现Collection接口的类都必须供给两个标准的机关函数:无参数的机关函数用于建立一个空的Collection,有一个Collection参数的机关函数用于建立一个新的Collection,这个新的Collection与传入的Collection有雷同的元素。后一个机关函数允许用户复制一个Collection。
如何遍历Collection中的每个元素?非论Collection的实际类型如何,它都支撑一个iterator()的办法,该办法返回一个迭代子,应用该迭代子便可一一接见Collection中每个元素。典范的用法以下:
Iterator it = collection.iterator(); // 得到一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的两个接口是List和Set。并发
List接口
List是有序的Collection,应用此接口能够或许正确的把握每一个元素插入的地位。用户能够或许应用索引(元素在List中的地位,相似于数组下标)来接见List中的元素,这相似于Java的数组。
和下面要提到的Set不合,List允许有雷同的元素。
除了具备Collection接口必备的iterator()办法外,List还供给一个listIterator()办法,返回一个ListIterator接口,和标准的Iterator接口比拟,ListIterator多了一些add()之类的办法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的经常使用类有LinkedList,ArrayList,Vector和Stack。ide
LinkedList类
LinkedList实现了List接口,允许null元素,双向循环链表的数据结构,因为是基于链表的,因此没法法实现随机访问的,只能顺序访问,是不一样步的,增删元素的速度很快。LinkedList可被用做堆栈(stack),队列(queue)或双向队列(deque)。函数
ArrayList类
内部是数组数据结构,是不一样步的,查询的速度快。它允许全部元素,包含null。ArrayList没有同步。
每一个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小,默认是10。这个容量可跟着络续添加新元素而主动增加,容量不够时默认增长两倍。若是指定容量,容量必须大于当前容量的三倍时才生效。当需要插入多量元素时,在插入前能够调用ensureCapacity办法来增加ArrayList的容量以进步插入效力。性能
Vector类
Vector非常相似ArrayList,然则Vector是同步的。默认容量是10,容量增长策略默认是增长一倍,若是指定容量则必须大于当前容量的两倍。线程
Stack 类
Stack持续自Vector,Stack 类表示后进先出(LIFO)的对象堆栈,Stack刚建立后是空栈。设计
List:
|--Vector:内部是数组数据结构,是同步的。增删,查询都很慢!
|--ArrayList:内部是数组数据结构,是不一样步的。替代了Vector。查询的速度快。
|--LinkedList:内部是链表数据结构,是不一样步的。增删元素的速度很快。
若是常常对数组作随机插入操做,特别是插入的比较靠前,那么LinkedList的性能优点就很是明显,而若是都只是末尾插入,则ArrayList更占据优点,若是须要线程安全,则使用Vector或者建立线程安全的ArrayList。在使用基于数组实现的ArrayList 和Vector 时咱们要指定初始容量,由于咱们在源码中也看到了,在添加时首先要进行容量的判断,若是容量不够则要建立新数组,还要将原来数组中的数据复制到新数组中,这个过程会减低效率而且会浪费资源。
Set接口
Set是一种无序的不包含重复元素的Collection,即随便率性的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
HashSet类
HashSet中存放的元素是无序的,底层是用HashMap实现的,其中key是要放入的元素,value是一个Object类型的名为PRESENT的常量,因为用到了散列函数,所以其存取速度是很是快的,在地址空间很大的状况下它的存取速度能够达到O(1)级。
LinkedHashSet
LinkedHashSet低层是LinkedHashMap,将数据按存储顺序或访问顺序排序。
TreeSet
TreeSet低层是TreeMap,将数据天然排序或按key排序。
Map接口
Map没有持续Collection接口,Map供给key到value的映射。一个Map中不能包含雷同的key,每一个key只能映射一个value。Map接口供给3种凑集的视图,Map的内容能够被算做一组key凑集,一组value凑集,或者一组key-value映射。
HashMap
HashMap是一种基于哈希表(hash table)实现的map,哈希表(也叫关联数组)一种通用的数据结构,由数组加链表实现,其概念也比较简单:key通过hash函数做用后获得一个槽(buckets或slots)的索引(index),槽中保存着咱们想要获取的值,以下图所示
线程非安全,而且容许key与value都为null值,不保证其内部元素的顺序,并且随着时间的推移,同一元素的位置也可能改变(resize的状况)
put、get操做的时间复杂度为O(1)。遍历其集合视角的时间复杂度与其容量(capacity,槽的个数)和现有元素的大小(entry的个数)成正比,因此若是遍历的性能要求很高,不要把capactiy设置的太高或把平衡因子(load factor,当entry数大于capacity*loadFactor时,会进行resize,reside会致使key进行rehash)设置的太低。
HashMap的桶数目,即Entry[] table数组的长度,因为数组是内存中连续的存储单元,它的空间代价是很大的,可是它的随机存取的速度是Java集合中最快的。咱们增大桶的数量,而减小Entry<Key,Value>链表的长度,来提升从HashMap中读取数据的速度。这是典型的拿空间换时间的策略。可是咱们不能刚开始就给HashMap分配过多的桶(即Entry[] table 数组起始不能太大),这是由于数组是连续的内存空间,它的建立代价很大,何况咱们不能肯定给HashMap分配这么大的空间,它实际到底可以用多少,为了解决这一个问题,HashMap采用了根据实际的状况,动态地分配桶的数量。
HashMap的权衡策略
要动态分配桶的数量,这就要求要有一个权衡的策略了,HashMap的权衡策略是这样的:
若是HashMap的大小> HashMap的容量(即Entry[] table的大小)*加载因子(经验值0.75),则HashMap中的Entry[] table 的容量扩充为当前的一倍;而后从新将之前桶中的Entry<Key,Value>链表从新分配到各个桶中。上述的 HashMap的容量(即Entry[] table的大小) * 加载因子(经验值0.75)就是所谓的阀值(threshold):
阀值(threshold)=容量(capacity)*加载因子(load factor)
容量(capacity):是指HashMap内部Entry[] table线性数组的长度,默认是16
加载因子(load factor):默认为0.75
阀值(threshold):当HashMap大小超过了阀值,HashMap将扩充2倍,而且rehash。
put()方法-向HashMap存储键值对<Key,Value>
a. 获取这个Key的hashcode值,根据此值肯定应该将这一对键值对存放在哪个桶中,即肯定要存放桶的索引;
b. 遍历所在桶中的Entry<Key,Value>链表,查找其中是否已经有了以Key值为Key存储的Entry<Key,Value>对象,
c1. 若已存在,定位到对应的Entry<Key,Value>,其中的Value值更新为新的Value值;返回旧值;
c2. 若不存在,则根据键值对<Key,Value> 建立一个新的Entry<Key,Value>对象,而后添加到这个桶的Entry<Key,Value>链表的头部。
d. 当前的HashMap的大小(即Entry<key,Value>节点的数目)是否超过了阀值,若超过了阀值(threshold),则增大HashMap的容量(即Entry[] table 的大小),而且从新组织内部各个Entry<Key,Value>排列。
HashMap在肯定Key是否在HashMap中存在的要求有两个:
1. Key值是否相等;
2. hashcode是否相等;
因此咱们在定义类时,若是重写了equals()方法,可是hashcode却没有保证相等,就会致使当使用该类实例做为Key值放入HashMap中,会出现HashMap“工做异常”的问题,会出现你不但愿的状况。
LinkedHashMap
LinkedHashMap是HashMap的一个子类,它保留插入的顺序,若是须要输出的顺序和输入时的相同,那么就选用LinkedHashMap。
LinkedHashMap是Map接口的哈希表和连接列表实现,具备可预知的迭代顺序。此实现提供全部可选的映射操做,并容许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
LinkedHashMap实现与HashMap的不一样之处在于,后者维护着一个运行于全部条目的双重连接列表。此连接列表定义了迭代顺序,该迭代顺序能够是插入顺序或者是访问顺序。
其实LinkedHashMap几乎和HashMap同样,不一样的是它定义了一个Entry<K,V> header,这个header不是放在Table里,它是额外独立出来的。LinkedHashMap经过继承hashMap中的Entry<K,V>,并添加两个属性Entry<K,V> before,after,和header结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序。
TreeMap
TreeMap的实现是红黑树算法的实现,TreeMap中全部的元素都保持着某种固定的顺序,若是你须要获得一个有序的结果你就应该使用TreeMap。
HashTable
Hashtable几乎能够等价于HashMap,除了HashMap是非synchronized的,并能够接受null(HashMap能够接受为null的键值(key)和值(value),而Hashtable则不行)。
CurrentHashMap
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap容许多个修改操做并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不一样部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不一样的部分,每一个段其实就是一个小的hash table,它们有本身的锁。只要多个修改操做发生在不一样的段上,它们就能够并发进行。