java中容器类数据结构主要在java.util包中。

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。

java常见数据结构整理的更多相关文章

  1. 转 Python常见数据结构整理

    http://www.cnblogs.com/jeffwongishandsome/archive/2012/08/05/2623660.html Python常见数据结构整理 Python中常见的数 ...

  2. Java常见Exception整理

    前言: 技术开发入坑近1年,摸打滚爬,各种升级打怪.因目前从事Java相关,故整理了一下并把常见的异常(Exception)贴出来,一来为了后续提醒自己,二来供即将入坑的朋友打一下预防针!A级(代码逻 ...

  3. Python常见数据结构整理

    Python中常见的数据结构可以统称为容器(container).序列(如列表和元组).映射(如字典)以及集合(set)是三类主要的容器. 一.序列(列表.元组和字符串) 序列中的每个元素都有自己的编 ...

  4. Java常见运算符整理

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/44724267 本文主要介绍Java中常见的运算符,重点介绍 ...

  5. Java常见数据结构

    HashMap深入浅出 HashMap数据结构 HashMap的本质就是一个数组加链表,数组默认长度是16,存储的元素达到总长度的75%就会扩容一倍.map.put(key,val),实际上就是根据h ...

  6. Java 常见BUG 整理

    1.BigDecimal初始化double 2.Integer   java对于-128到127之间的数,会进行缓存,这个范围的Integer对象是同一个! == 是ok,但是超出这个范围就不可以用 ...

  7. java常见数据结构举例

    1. ArrayList(参考) import java.util.*; public class Test{ public static void main(String [] args){ Arr ...

  8. Java常见错误整理(一)

    1.配置完Java环境变量之后,仍然不能使用java命令. 解决方法: 如果是Windows10系统出现此问题,是因为个别Windows10系统不识别“JAVA_HOME”环境变量,将path中所有的 ...

  9. java 常见数据结构

    1)tree 2) queue 3) list 4) stack 5) heap 6) map

随机推荐

  1. [转]MySQL单列索引和组合索引的区别介绍

    FROM : http://database.ctocio.com.cn/353/11664853.shtml MySQL单列索引是我们使用MySQL数据库中经常会见到的,MySQL单列索引和组合索引 ...

  2. SQLServer 日期函数大全 SQLServer 时间函数大全

    原文地址:https://www.cnblogs.com/zhangpengnike/p/6122588.html 一.统计语句 1.--统计当前[>当天00点以后的数据] SELECT * F ...

  3. String、StringBuffer与StringBuilder之间区别(转)

    原文链接:String.StringBuffer与StringBuilder之间区别 最近学习到StringBuffer,心中有好些疑问,搜索了一些关于String,StringBuffer,Stri ...

  4. caffe添加python数据层

    caffe添加python数据层(ImageData) 在caffe中添加自定义层时,必须要实现这四个函数,在C++中是(LayerSetUp,Reshape,Forward_cpu,Backward ...

  5. 57. 三数之和 &&

    题目 57. 三数之和 给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组. 样例 如S = {-1 0 1 2 -1 -4}, 你需要返回的 ...

  6. AngulerJS学习(五)按需动态载入文件

    在此之前我么年首先要先了解几个东西: $q 简单介绍: $q:主要解决的是异步编程的问题,是指描写叙述通过一个承诺行为与对象代表的异步运行的行动结果的交互.可能会也可能不会再不论什么时候完毕. 我们通 ...

  7. Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别

    为什么要用jsonp? 相信大家对跨域一定不陌生,对同源策略也同样熟悉.什么,你没听过?没关系,既然是深入浅出,那就从头说起. 假如我写了个index页面,页面里有个请求,请求的是一个json数据(不 ...

  8. Tensorflow中张量数据类型的转换

    https://blog.csdn.net/Tramac/article/details/74942587 字符串转为数字: tf.string_to_number (string_tensor, o ...

  9. 【R】自定义函数方法

  10. RabbitMQ的应用场景以及基本原理介绍 【转】

    http://blog.csdn.net/whoamiyang/article/details/54954780 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 背景 应用 ...