JAVA集合

常用集合

set

Set不按特定方式进行排序,并且没有重复的对象,它的有些实现类能对集合中的对象按照特定的顺序排序。主要有两个实现类:HashSet和TreeSet

HashSet按照哈希算法来存取集合中的对象,存取速度比较快。

TreeSet实现了SortSet接口,具有排序功能。

List

List主要特征是以线性方式存储,集合中允许存放重复对象主要实现类包括ArrayList和LinkList

ArrayList代表长度可变的数组,允许对元素进行快速的随机访问,但是向ArrayList中插入与删除元素较慢。

LinkList在实现中采用链表的结构,插入和删除元素较快,随机访问则相对较慢。

LinkList单独具有addFirst(),addLast(),getFirst(),getLast(),removeFirst(),removeLast()等方法这些方法使得LinkList可以作为堆栈,队列和双向队列使用

Queue

Queue队列中的对象按照先进先出的规则来排列。在队列末尾添加元素,在队列的头部删除元素。可以有重复对象。

双向队列Deque则允许在队列的尾部和头部添加和删除元素。

因为LinkList可以作为双向队列使用,所以Queue和Deque使用比较少

Map

HashMap不保证映射的顺序,特别是它不保证该顺序恒久不变。(也就是无序)

HashMap底层基于散列表实现,由数组加链表组成,数组是HashMap的主体,链表用来解决Hash冲突

采用key的hashCode()方法结合数组长度通过无符号右移>>> 异或^ 计算出数组索引,通过链表法解决哈希冲突(如果key值equals()则替换旧的value),

平方去中法 取余数 伪随机数法

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

// aka 16 默认初始容量是2的n次幂,可以减少哈希碰撞3&(8-1)=3 2&(8-1)=2 | 3&(9-1)=0 2&(9-1)=0

DEFAULT_LOAD_FACTOR=0.75 代表数组满的程度,如果小 则数组利用率不高,太大 则更容易产生Hash碰撞(每个桶的链表会变多)

JDK1.8之后链表长度超过8,并且数组长度大于64会进化成红黑树(红黑树查询O(logn) 链表O(n))

JDK1.7的时候HashMap是头插,扩容的时候链表顺序会倒序。 1.8时是尾插,扩容后链表顺序不变 (也可以防止并发扩容的时候 变成环链)。

TreeMapTreeMap基于红黑树(Red-Black tree)的 NavigableMap 实现,有序。

HashMap是使用hashCode和equals方法去重的。而TreeMap依靠Comparable或Comparator来实现key去重

并发类容器(集合)

  在java集合框架中,Set List Queue Map的实现类都没有采取同步机制。在单线程环境中,这种实现方式会提高操纵集合的效率,java虚拟机不必会因为管理用不锁而产生额外的开销。

在多线程环境中,可能会有多个线程同时操纵一个集合,比如一个线程在为集合排序,而另外一个线程正不断的向集合中添加新的元素。

为了避免并发问题,可以直接采用java.util.concurrent并发包提供线程安全的集合。列如

  ConcurrentHashMap  、ConcurrentSkipListMapConcurrentSkipListSetConcurrentLinkedQueue

这些集合的底层实现采用了复杂的算法,保证多线程访问集合时,既能保证线程之间的同步,又具有高效的并发性能。

  Hashtable:

    Hashtable,用作键的对象必须实现 hashCode 方法和 equals 方法。HashTable 由于所有方法都加了 synchronized 关键字所以是线程安全的。

    如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。 

历史集合类 :Vector Hashtable Stack Enumeration 在实现中都采用了同步机制,并发性能较差,因此不提倡使用它们。

  ConcurrentHashMap:

JDK7:ConcurrentHashMap采用了分段锁的,把容器默认分成16段,put值的时候 只是锁定16断中的一个部分,就是把锁给细化了

JDK8:采用的CAS自旋

疑问:HashMap有线程安全的ConcurrentHashMap 但是TreeMap为什么没有ConcurrentTreeMap

     因为CAS操作用在红黑树实现起来太复杂 

   所以用ConcurrentSkipListMap用CAS实现排序(跳表代替Tree) 

   跳表:在链表的基础上一层一层的加一些个关键元素的链表,加了个索引。跳表的查找效率比链表本身要高,同时它的CAS实现难度比TreeMap容易很多

HashMap为什么是线程不安全的

1.首先我们解释下HashMap的扩容:当HashMap的元素个数超过:数组长度乘以负载因子DEFAULT_LOAD_FACTOR(16*0.75)=12时,就会把数组长度扩大为16X2=32

关于死循环的问题,在Java8中个人认为是不存在了,在Java8之前的版本中之所以出现死循环是因为在resize的过程中对链表进行了倒序处理(头插);在Java8中不再倒序处理(尾插),自然也不会出现死循环。

 

posted @ 2018-07-25 22:00  palapala  阅读(295)  评论(0编辑  收藏  举报