zoukankan      html  css  js  c++  java
  • java集合框架分析

      接口框架:

      java集合框架总体UML图 - dryr - 学而时习之,温故而知新

      集合框架中包含List,Queue,Set和Map这四大块,注意:Map虽然属于集合框架,但Map接口并不从Collection接口扩展。

      一个Map提供了通过Key对Map中存储的Value进行访问,也就是说它操作的都是成对的对象元素,比如put()和get()方法,而这是一个Set或List所不就具备的(它们是add()和remove())。当然在需要时,你可以由keySet()方法或values()方法从一个Map中得到键的Set集或值的Collection集。

      1.Collection

      Collection接口提供了一组操作成批对象的方法
      它提供了基本操作如添加、删除。它也支持查询操作如是否为空isEmpty()方法等。为了支持对Collection进行独立操作,Java的集合框架给出了一个Iterator,它使得你可以泛型操作一个Collection,而不需知道这个Collection的具体实现类型是什么。

      在你实现一个集合接口时,你可以很容易的在你不想让用户使用的方法中抛出UnsupportOperationException来告诉使用者这个方法当前没有实现。

      Java的容器类库还有一种Fail fast的机制。modCount就是保证每次操作后都会加一,Iterator操作会检查此值来控制并发,比如你正在用一个Iterator遍历一个容器中的对象,这时另外一个线程或进程对那个容器进行了修改,那么再用next()方法时可能会有灾难性的后果,而这是你不愿看到的,这时就会引发一个ConcurrentModificationException例外。这就是fail-fast。所以要在遍历容器时,删除其中的元素,必须调用Iterator的remove来删除才不会抛出异常。 

      2.List

      List接口对Collection进行了简单的扩充。List在Collection的基础上添加了大量方法,使之能在序列中间插入和删除元素。

      ArrayList从其命名中可以看出它是一种类似数组的形式进行存储,因此它的随机访问速度极快。

      而LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。在具体应用时可以根据需要自由选择。其中放置元素的是Node内部类,包含指向前和后的两个指针。

      Vector:线程是安全的,也就是说是同步的。ArrayList不安全。

      前面说的Iterator只能对容器进行向前遍历,而ListIterator则继承了Iterator的思想,并提供了对List进行双向遍历的方法。

      ArrayList的扩充实现(add):

      1.检查是否容量不够

      2.扩容到原来的1.5倍

      3.如果新容量小于需要的容量,则使用需要容量大小, 并检查是否为容量是否为负,则使用Integer.MAX来代替。

      4.使用Arrays.copyOf -> System.arraycopy来进行底层数组的复制。

      3.Set

      Set接口也是Collection的一种扩展。
      与List不同的是,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。
      HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法,它使用了前面说过的哈希码的算法。  而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。有时多个类具有相同的排序算法,那就不需要在每分别重复定义相同的排序算法,只要实现Comparator接口即可。
      集合框架中还有两个很实用的公用类:Collections和Arrays。Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。
      其实Set都是对Map的封装,因为HashMap是hash映射的方法,TreeMap是根据红黑树来实现的,都是不允许重复元素的,所以Set就是把Map的put和get方法封装成add和remove方法。

      4.Map

      Map是一种把键对象和值对象进行关联的容器。
      键和值的关联很简单,用put(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。Map包装元素的是Entry内部类,其中包含了key、value、hash值和指针
      HashMap:基于hash表的实现。比Hashtable轻量级,(用它来代替Hashtable。)提供时间恒定的插入与查询。在构造函数种可以设置hash表的capacity和load factor。可以通过构造函数来调节其性能。
      HashMap冲突的情况:
      //将新增元素放到i(hash值后的数组下标)位置,并让它的next指向旧的元素。
      table[bucketIndex] = new Entry<>(hash, key, value, e);
      TreeMap:基于红黑树数据结构的实现。当你查看键或pair时,会发现它们时按顺序(根据Comparable或Comparator,我们过一会讲)排列的。TreeMap的特点时,你所得到的是一个有序的Map。TreeMap是Map中唯一有subMap()方法的实现。这个方法能让你获取这个树中的一部分。
      LinkedHashMap:很像HashMap,但是用Iterator进行遍历的时候,它会按插入顺序或最先使用的顺序(least-recently-used(LRU)order)进行访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用链表来保存内部顺序(Entry),因此速度会更快。
      WeakHashMap:一个weak key的Map,是为某些特殊问题而设计的。它能让Map释放其所持有的对象。如果某个对象除了在Map当中充当键之外,在其他地方都没有其reference的话,那它将被当作垃圾回收。
      HashMap扩充/调整性能
      HashMap 的实例有两个参数影响其性能:初始容量capacity和加载因子loadcafe容量哈希表中桶的数量,加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。
      当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构,进行数据迁移,复制所有数据到新的结构中),从而哈希表将具有大约两倍的桶数。
      
     1     /**
     2      * Transfers all entries from current table to newTable.
     3      */
     4     void transfer(Entry[] newTable, boolean rehash) {
     5         int newCapacity = newTable.length;
     6         for (Entry<K,V> e : table) {
     7             while(null != e) {
     8                 Entry<K,V> next = e.next;
     9                 if (rehash) {
    10                     e.hash = null == e.key ? 0 : hash(e.key);
    11                 }
    12                 int i = e.hash & (newCapacity-1);
    13                 e.next = newTable[i];
    14                 newTable[i] = e;
    15                 e = next;
    16             }
    17         }
    18     }

      5.Queue

      add、remove和element(返回队列头部)操作在你试图为一个已满的队列增加元素或从空队列取得元素时抛出异常。

      offer、poll、peek方法在无法完成任务时只是给出一个出错示而不会抛出异常。

      Deque(接口):(deque,全名double-ended queue)是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。LinkedList实现了它,因为LinkedList本来就具有双向操作的性质。另外还有一个实现是ArrayDeque,它是用数组来实现Deque的双向操作。

      BlockingQueue(接口):作为线程容器,可以为线程同步提供有力的保障。对它的某些操作是阻塞的,如果BlockQueue是空的,BlockingQueue取东西(take或poll(time))的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒.同样,如果BlockingQueue是满的,任何试图往里存东西(put(E))的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作.

      1)ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.

      2)LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的

      3)PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.

      4)SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的.

      Block的阻塞实现(put(E)):

    final ReentrantLock lock;
    /** Condition for waiting puts */
    private final Condition notFull;

         final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                while (count == items.length)
                    notFull.await();
                insert(e);
            } finally {
                lock.unlock();
            }

      首先,用ReentrantLock 来锁定以此控制并发。

      java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。

      synchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是隐式的, 只要线程运行的代码超出了synchronized语句块范围, 锁就会被释放.

      而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁, 这为获取锁和释放锁不出现在同一个块结构中, 以及以更自由的顺序释放锁提供了可能。可中断,可定时

      reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁(因为锁是显示的释放,所以可能一个线程获得锁后再次试图获取锁),那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。

      reentrant 锁还提供了公平和非公平两种锁,公平锁保证锁的等待队列是先进先获,而非公平有可能有插队的现象。

      然后,使用Conditon来进行阻塞等待。

      Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其newCondition() 方法。

      当线程调用condition.wait的时候,当前线程持有的可重入锁会释放;当其他线程调用condition.signal的时候,该线程重新获得锁并复原。

      Condition的优势是支持多路等待,就是我可以定义多个Condition,每个condition控制线程的一条执行通路。传统方式只能是一路等待。

      当有其它的线程拿走元素时,就会触发condition.signal唤醒等待的线程。

      6.AbstractCollection 

      此类提供了 Collection 接口的骨干实现,从而最大限度地减少了实现此接口所需的工作。

      要实现一个不可修改的 collection,程序员只需扩展此类,并提供 iterator 和 size 方法的实现。(iterator 方法返回的迭代器必须实现 hasNext 和 next。)

      其它的Abstract的抽象类都是这个原理,为了减少实现接口所需的工作。

      以下是List和Set的抽象类架构:

      

      以下是Map的抽象类架构:

      

    参考:百科

    http://www.iteye.com/topic/1114847

    http://dryr.blog.163.com/blog/static/58211013201132352356657/

  • 相关阅读:
    routine的加载
    Forms Authentication and Role based Authorization: A Quicker, Simpler, and Correct Approach
    How does Request.IsAuthenticated work?
    细说ASP.NET Forms身份认证
    IIS URL Rewriting and ASP.NET Routing
    Razor syntax reference for ASP.NET Core
    win10 合并磁盘 disk Acronis Disk Director
    bad object refs/remotes/origin/HEAD
    https://www.atlassian.com/git/tutorials/git-gc
    c++ 中文字符串处理方法
  • 原文地址:https://www.cnblogs.com/jslee/p/3433194.html
Copyright © 2011-2022 走看看