• 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,因此必须捕获该异常。
     
  • 相关阅读:
    TCP中的三次握手与四次挥手
    C++中的访问控制与封装
    C++中的类定义
    Verilog学习笔记设计和验证篇(三)...............同步有限状态机的指导原则
    Verilog学习笔记简单功能实现(三)...............同步有限状态机
    Verilog学习笔记设计和验证篇(二)...............同步有限状态机
    Verilog学习笔记设计和验证篇(一)...............总线和流水线
    Verilog学习笔记简单功能实现(二)...............全加器
    Verilog HDL模型的不同抽象级别
    Verilog学习笔记简单功能实现(一)...............D触发器
  • 原文地址:https://www.cnblogs.com/novalist/p/6397718.html
Copyright © 2020-2023  润新知