前不久面试官让我说一下怎么理解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同步。

      看源代码非常easy理解。

      Collections部分源代码:

      //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);}
      }
      //...
      }
    • List (列表)

      • ArrayList、Vector是线性表。使用Object数组作为容器去存储数据的,加入了非常多方法维护这个数组。使其容量能够动态增长。极大地提升了开发效率。它们明显的差别是ArrayList是非同步的。Vector是同步的。不用考虑多线程时应使用ArrayList来提升效率。

        ArrayList、Vector 部分源代码:

        //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;
        }
      • LinkedList是链表,略懂数据结构就知道事实上现原理了。链表随机位置插入、删除数据时比线性表快,遍历比线性表慢。

        双向链表原理图:



        LinkedList部分源代码:

        //源代码非常清晰地表达了原理图
        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;
        }
        }

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

  • Map(存储键值对。key唯一)

    • HashMap结构的实现原理是将put进来的key-value封装成一个Entry对象存储到一个Entry数组中,位置(数组下标)由key的哈希值与数组长度计算而来。假设数组当前下标已有值,则将数组当前下标的值指向新加入的Entry对象。

      有点晕。看图吧:



      看完图再看源代码。非常清晰。都不须要凝视。

      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;
      }
    • TreeMap是由Entry对象为节点组成的一颗红黑树,put到TreeMap的数据默认按key的自然顺序排序,new TreeMap时传入Comparator自己定义排序。红黑树网上非常多资料,我讲不清,这里就不介绍了。

  • Set(保证容器内元素唯一性)

    之所以先讲Map是由于Set结构事实上就是维护一个Map来存储数据的。利用Map结构key值唯一性

    HashSet部分源代码:

    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;
    }
    }

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

JAVA经常使用数据结构及原理分析的更多相关文章

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

    JAVA常用数据结构及原理分析 http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balaba ...

  2. (6)Java数据结构-- 转:JAVA常用数据结构及原理分析

    JAVA常用数据结构及原理分析  http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balab ...

  3. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

  4. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  5. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  6. Java 远程通讯技术及原理分析

    在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI.MINA.ESB.Burlap.Hessian.SOAP.EJB和JMS等,这些 ...

  7. Java远程通讯技术及原理分析

    在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI.MINA.ESB.Burlap.Hessian.SOAP.EJB和JMS等,这些 ...

  8. Java线程池应用及原理分析(JDK1.8)

    目录 一.线程池优点 二.线程池创建 三.任务处理流程 四.任务缓存队列及排队策略 五.任务拒绝策略 六.线程池关闭 七.线程池实现原理 八.静态方法创建线程池 九.如何确定线程池大小 一.线程池优点 ...

  9. Java,JDK动态代理的原理分析

    1. 代理基本概念: 以下是代理概念的百度解释:代理(百度百科) 总之一句话:三个元素,数据--->代理对象--->真实对象:复杂一点的可以理解为五个元素:输入数据--->代理对象- ...

随机推荐

  1. 【Luogu4389】付公主的背包

    题目 传送门 解法 答案显然是\(n\)个形如\(\sum_{i \geq 1} x^{vi}\)的多项式的卷积 然而直接NTT的时间复杂度是\(O(nm\log n)\) 我们可以把每个多项式求\( ...

  2. 《疯狂Python讲义》重要笔记--变量

    一个Python解释器 接下来的旅程——你需要下载好Python,Python解释器通常放在 /usr/local/bin/python3.7 ; 在Unix系统的bash中输入 where pyth ...

  3. Python 42 mysql用户管理 、pymysql模块

    一:mysql用户管理 什么是mysql用户管理 mysql是一个tcp服务器,应用于操作服务器上的文件数据,接收用户端发送的指令,接收指令时需要考虑到安全问题, ATM购物车中的用户认证和mysql ...

  4. HDFS Shell命令操作与java代码操作

    (一)编程实现以下功能,并利用 Hadoop 提供的 Shell 命令完成相同任务: (1)     向 HDFS 中上传任意文本文件,如果指定的文件在 HDFS 中已经存在,则由用户来指定是追加到原 ...

  5. 安装Windows服务,一直提示系统正在关机的错误。

    错误截图如下: 问题概况: 在本机安装没问题,程序没问题. 安装到公司的测试环境就报错了!以管理员身份运行也不行. 解决方案: 1.最后发现是360安全防护中心拦截了.具体解决过程如下: 2.进入36 ...

  6. py2exe打包遇到的问题

    py2exe打包python成.exe文件 打包过程和结果 1.创建setup脚本打包文件,其中设置打包的属性和方法.注意:尽量将被打包文件和此打包脚本放在同目录下(因为在尝试非同目录下时,出现了非可 ...

  7. 【原创】python中文编码问题深入分析(三):python2.7文件读写中文编码问题

    上一篇文章介绍和分析了python2.7中使用print遇到的中文编码问题的原因和解决方案,本篇主要介绍一下python2.7中执行文件读写可能遇到的编码问题. 1.文件读取 假如我们读取一个文件,文 ...

  8. Yearning + Inception SQL审核平台搭建

    Yearning 安装: 安装Nginxyum install nginx -y 按照顺序安装MySQLmysql-community-common-5.7.22-1.el6.x86_64.rpmmy ...

  9. 解决Cannot change version of project facet Dynamic Web M 3.0

    解决Cannot change version of project facet Dynamic Web M 3.0 dynamic web module 版本之间的区别: Servlet 3.0 D ...

  10. NOPI读取Word模板并保存

    安装NPOI 可以在 程序包管理器控制台中输入 PM> Install-Package NPOI 会下载最新版本NPOI ----------------------------引用了NPOI- ...