Java集合框架

  

Java集合框架图一览:
一、ArrayList扩容机制
ArrayList底层采用Object类型的数组实现,当使用不带参数的构造方法生成ArrayList对象时,实际上会在底层生成一个长度为10的Object类型数组。
 /** 
     * Increases the capacity to ensure that it can hold at least the 
     * number of elements specified by the minimum capacity argument. 
     * 
     * @param minCapacity the desired minimum capacity 
     */  
    private void grow(int minCapacity) {  
        // overflow-conscious code  
        int oldCapacity = elementData.length;  
        int newCapacity = oldCapacity + (oldCapacity >> 1);  
        if (newCapacity - minCapacity < 0)  
            newCapacity = minCapacity;  
        if (newCapacity - MAX_ARRAY_SIZE > 0)  
            newCapacity = hugeCapacity(minCapacity);  
        // minCapacity is usually close to size, so this is a win:  
        elementData = Arrays.copyOf(elementData, newCapacity);  
    }  

  int newCapacity = oldCapacity + (oldCapacity >> 1)
  newCapacity 就是扩容的新组数大小。而扩容的大小是根据原有旧数组大小来决定的。
  oldCapacity >>1。就是右移1位,这里会换算成二进制来进行。就是把十进制对应的数字换成二进制后往右边移一位。所以跟除以2差不多。所以扩容为原来的1.5倍。
  但是右移一位的时候,如果非偶数,也就是说二进制数的最低位为1,则会舍弃,就成为了除2-1。对于CPU而言,右移一位要比除2快很多
 
二、HashMap为什么线程不安全?

  先来了解一下HashMap的底层存储结构,HashMap底层是一个Entry数组,一旦发生Hash冲突的的时候,HashMap采用拉链法解决碰撞冲突,Entry内部的变量:

  1. final Object key;
  2. Object value;
  3. Entry next;
  4. int hash;
  通过Entry内部的next变量可以知道使用的是链表,这时候我们可以知道,如果多个线程,在某一时刻同时操作HashMap并执行put操作,而有大于两个key的hash值相同,如图中a1、a2,这个时候需要解决碰撞冲突,而解决冲突的办法上面已经说过,对于链表的结构在这里不再赘述,暂且不讨论是从链表头部插入还是从尾部初入,这个时候两个线程如果恰好都取到了对应位置的头结点e1,而最终的结果可想而知,a1、a2两个数据中势必会有一个会丢失,如图所示:
  可以看到扩容方法也不是同步的,通过代码我们知道在扩容过程中,会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。 当多个线程同时检测到总数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。
 
  HashMap等线程不安全的容器,用在多线程读/写的场合,导致HashMap的方法调用形成死循环
    多线程场合,对共享变量没有进行保护,导致数据混乱 

    系统的问题分为几种类型:
      ①在堆栈中能够表现出问题的,使用线程堆栈进行定位
      ②无法再线程中留下痕迹的问题定位,需要依赖于一个好的日志设计
    HashMap的ConcurrentModificationException  如果hash map在迭代的同时,被其他线程修改,则会抛出一个ConcurrentModificationException。
    HashMap被多线程访问下,可能导致put或者get方法block(或者死循环)
 
三、HashMap,put()与get()的复杂度为多少?
  默认复杂度都为O(1)
 
四、HashMap与HashTable与ConcurrentHashMap之间的区别与关系
  1. HashMap不是线程同步、线程不安全,不适合应用于多线程并发环境下,而ConcurrentHashMap是线程安全的集合容器
  2. ConcurrentHashMap的设计有点特别,表现在多个线程操作上。它不用做外的同步的情况下默认同时允许16个线程读和写这个Map容器。因为其内部的实现剥夺了锁,使它有很好的扩展性。不像HashTable和Synchronized Map,ConcurrentHashMap不需要锁整个Map,相反它划分了多个段(segments),要操作哪一段才上锁那段数据。
  3. HashMap与HashTable——HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。

 

五、集合类
接口 简述 实现 操作特性 成员要求
Set 成员不能重复 HashSet 外部无序地遍历成员 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeSet 外部有序地遍历成员;附加实现了SortedSet, 支持子集等要求顺序的操作 成员要求实现caparable接口,或者使用 Comparator构造TreeSet。成员一般为同一类型。
LinkedHashSet 外部按成员的插入顺序遍历成员 成员与HashSet成员类似
List 提供基于索引的对成员的随机访问 ArrayList 提供快速的基于索引的成员访问,对尾部成员的增加和删除支持较好 成员可为任意Object子类的对象
LinkedList 对列表中任何位置的成员的增加和删除支持较好,但对基于索引的成员访问支持性能较差 成员可为任意Object子类的对象
Map 保存键值对成员,基于键找值操作,compareTo或compare方法对键排序 HashMap 能满足用户对Map的通用需求 键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeMap 支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作 键成员要求实现caparable接口,或者使用Comparator构造TreeMap。键成员一般为同一类型。
LinkedHashMap 保留键的插入顺序,用equals 方法检查键和值的相等性 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
IdentityHashMap 使用== 来检查键和值的相等性。 成员使用的是严格相等
WeakHashMap 其行为依赖于垃圾回收线程,没有绝对理由则少用  
 
六、Collection和Collections
Collection是一系列单值集合类的父接口,提供了基本的一些方法,而Collections则是一系列算法的集合。里面的属性和方法基本都是static的,也就是说我们不需要实例化,直接可以使用类名来调用。下面是Collections类的一些功能列表:
生成单元素集合:
    Collections中的单元素集合指的是集合中只有一个元素而且集合只读。
    Collections.singletonList——用来生成只读的单一元素的List
    Collections.singletonMap——用来生成只读的单Key和Value组成的Map
Collections.singleton——用来生成只读的单一元素的Set
 
七. ArrayList、LinkedList、Vector的区别。
LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));
 
ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
 
Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。
 
原文地址:https://www.cnblogs.com/novalist/p/6397718.html