• JAVA常用数据结构及原理分析


    JAVA常用数据结构及原理分析

    http://www.2cto.com/kf/201506/412305.html

    前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balabala讲了一堆,现在总结一下。

    java.util包中三个重要的接口及特点:List(列表)、Set(保证集合中元素唯一)、Map(维护多个key-value键值对,保证key唯一)。其不同子类的实现各有差异,如是否同步(线程安全)、是否有序。
    常用类继承树:
    这里写图片描述

    以下结合源码讲解常用类实现原理及相互之间的差异。

    Collection (所有集合类的接口)
    List、Set都继承自Collection接口,查看JDK API,操作集合常用的方法大部分在该接口中定义了。
    这里写图片描述

    Collections (操作集合的工具类)
    对于集合类的操作不得不提到工具类Collections,它提供了许多方便的方法,如求两个集合的差集、并集、拷贝、排序等等。
    由于大部分的集合接口实现类都是不同步的,可以使用Collections.synchronized*方法创建同步的集合类对象。
    如创建一个同步的List:
    List synList = Collections.synchronizedList(new ArrayList());
    其实现原理就是重新封装new出来的对象,操作对象时用关键字synchronized同步。看源码很容易理解。
    Collections部分源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <code class="language-java hljs ">//Collections.synchronizedList返回的是静态类SynchronizedCollection的实例,最终将new出来的ArrayList对象赋值给了Collection<e> c。
    static class SynchronizedCollection<e> implements Collection<e>, Serializable {
     
            final Collection<e> c;  // Backing Collection
            final Object mutex;     // Object on which to synchronize
     
            SynchronizedCollection(Collection<e> c) {
                if (c==null)
                    throw new NullPointerException();
                this.c = c;
                mutex = this;
            }
            //...
            public boolean add(E e) {
                //操作集合时简单调用原本的ArrayList对象,只是做了同步
                synchronized (mutex) {return c.add(e);}
            }
            //...
    }</e></e></e></e></e></code>

    List (列表)

    ArrayList、Vector是线性表,使用Object数组作为容器去存储数据的,添加了很多方法维护这个数组,使其容量可以动态增长,极大地提升了开发效率。它们明显的区别是ArrayList是非同步的,Vector是同步的。不用考虑多线程时应使用ArrayList来提升效率。
    ArrayList、Vector 部分源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <code class="language-java hljs ">//ArrayList.add
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //可以看出添加的对象放到elementData数组中去了
        elementData[size++] = e;
        return true;
    }
    //ArrayList.remove
    public E remove(int index) {
        rangeCheck(index);
     
        modCount++;
        E oldValue = elementData(index);
     
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //移除元素时数组产生的空位由System.arraycopy方法将其后的所有元素往前移一位,System.arraycopy调用虚拟机提供的本地方法来提升效率
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
     
        return oldValue;
    }
     
    //Vector add方法上多了synchronized关键字
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }</code>

    LinkedList是链表,略懂数据结构就知道其实现原理了。链表随机位置插入、删除数据时比线性表快,遍历比线性表慢。
    双向链表原理图:
    这里写图片描述
    LinkedList部分源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <code class="language-java hljs ">//源码很清晰地表达了原理图
    public class LinkedList<e>
    extends AbstractSequentialList<e>
    implements List<e>, Deque<e>, Cloneable, java.io.Serializable
    {
        //头尾节点
        transient Node<e> first;
        transient Node<e> last;
    }
    //节点类
     private static class Node<e> {
        //节点存储的数据
        E item;
        Node<e> next;
        Node<e> prev;
        Node(Node<e> prev, E element, Node<e> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }</e></e></e></e></e></e></e></e></e></e></e></code>

    由此可根据实际情况来选择使用ArrayList(非同步、非频繁删除时选择)、Vector(需同步时选择)、LinkedList(频繁在任意位置插入、删除时选择)。

    Map(存储键值对,key唯一)

    HashMap结构的实现原理是将put进来的key-value封装成一个Entry对象存储到一个Entry数组中,位置(数组下标)由key的哈希值与数组长度计算而来。如果数组当前下标已有值,则将数组当前下标的值指向新添加的Entry对象。
    有点晕,看图吧:
    这里写图片描述
    看完图再看源码,非常清晰,都不需要注释。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <code class="language-java hljs ">public class HashMap<k,v>
    extends AbstractMap<k,v>
    implements Map<k,v>, Cloneable, Serializable
    {
        transient Entry<k,v>[] table;
        public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key);
            int i = indexFor(hash, table.length);
            //遍历当前下标的Entry对象链,如果key已存在则替换
            for (Entry<k,v> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
           addEntry(hash, key, value, i);
            return null;
        }
    }
    static class Entry<k,v> implements Map.Entry<k,v> {
        final K key;
        V value;
        Entry<k,v> next;
        int hash;
    }</k,v></k,v></k,v></k,v></k,v></k,v></k,v></k,v></code>

    TreeMap是由Entry对象为节点组成的一颗红黑树,put到TreeMap的数据默认按key的自然顺序排序,new TreeMap时传入Comparator自定义排序。红黑树网上很多资料,我讲不清,这里就不介绍了。

    Set(保证容器内元素唯一性)
    之所以先讲Map是因为Set结构其实就是维护一个Map来存储数据的,利用Map结构key值唯一性
    HashSet部分源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <code class="language-java hljs ">public class HashSet<e>
    extends AbstractSet<e>
    implements Set<e>, Cloneable, java.io.Serializable
    {   
     
        //无意义对象来作为Map的value
        private static final Object PRESENT = new Object();
        public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    }</e></e></e></code>

    HashSet、TreeSet分别默认维护一个HashMap、TreeMap。

  • 相关阅读:
    dhl:有用的sql语句(我用到的)更新中....
    dhl:给Button设背景图片
    遍历一个类中的每一个属性、方法、公共字段
    swf、wmv、mov、RM几种常见格式视频播放器代码!
    理解Windows中的路由表和默认网关
    主/辅DNS服务器详细配置
    用组策略彻底禁止USB存储设备、光驱、软驱、ZIP软驱
    DHCP中继原理及配置--路由器
    路由器NAT功能配置简介
    网络负载平衡群集
  • 原文地址:https://www.cnblogs.com/handsome1013/p/5548872.html
Copyright © 2020-2023  润新知