Java集合框架图解

Java集合框架是工作中经常用到的,是很基础但是很重要的内容,本文将详述Java集合框架的内容。

Java集合框架图

  • 下面是亲手绘制的一张Java集合框架图,列出了集合框架中常用的类。
  • 集合其实就是一个用来盛放数据的容器,位于java.util包下,多线程的集合类位于java.util.concurrent包下。
  • Map接口并不属于Collection,Java集合类是由Collection和Map派生出来的。
    Java集合框架

集合和数组的区别

  • 数组长度固定并且无法保存映射关系的数据。集合类可以用来保存数量不确定并且具有映射关系的数据。
  • 数组可以保存基本类型的数据,也可以是引用类型的数据;而集合只能保存对象。

List集合

  • List集合是一组有序可重复集合,元素都有索引,按照添加顺序排列,可以通过索引(LinkedList使用链表)快速查找,其中包含了三个常用的类,分别是ArrayList,Vector,LinkedList。下面我们逐一分析。
  • ArrayList
    • 底层使用的是数组;
    • 根据数组索引查找元素,所以查询快;增删因为要移动元素位置,所以增删慢;
    • 元素按照插入的顺序排列,元素可以重复;
    • 非线程安全
    • 它的结构图如下所示:
      ArrayList结构图
    • 注意点:如果我们在使用ArrayList的时候,提前知道会放入多少长度元素的时候,可以提前指定集合的容量,这样就会减少添加数据的时候扩容的次数,提高性能(当然数据量大时候效果更好)。比如下面这段程序,时间差异达7倍:
      	long date1 = System.currentTimeMillis();
      	ArrayList<Integer> list = new ArrayList<>();
      	for (int i = 0; i < 10000000; i++) {
      		list.add(i);
      	}
      	System.out.println(System.currentTimeMillis() - date1); //不指定容量用时:2935
      
      	long date2 = System.currentTimeMillis();
      	ArrayList<Integer> list2 = new ArrayList<>(10000001);
      	for (int i = 0; i < 10000000; i++) {
      		list2.add(i);
      	}
      	System.out.println(System.currentTimeMillis() - date2); //指定容量用时:451
      
  • Vector
    • 底层使用的是数组;
    • 根据数组索引查找元素,所以查询快;增删因为要移动元素位置,所以增删慢;
    • 元素按照插入的顺序排列,元素可以重复;
    • 线程安全(对方法整体使用synchronized关键字加锁,效率不高)
    • 它的结构图和ArrayList一样如下所示:
      在这里插入图片描述
  • LinkedList
    • 底层使用的双向链表;
    • 使用链表查找元素,所以查询慢,增删只需要替换掉指针的指向即可,所以增删快;
    • 元素按照插入的顺序排列,元素可以重复;
    • 它的结构图如下所示:
      LinkedList数据结构图

Set集合

  • Set集合是一组无序不可重复集合(LinkedHashSet元素有序),其中包含了三个常用的类,分别是HashSet,TreeSet,LinkedHashSet。下面我们逐一分析。

  • HashSet:

    • 底层是用HashMap数据结构,以存入的元素为Key,因为不关心value,所以value为同一个object,Key元素可以为null。
    • 因为HashMap的Key不可重复,而我们只关心Key,所以HashSet元素不可重复,元素无序
    • 因为内部使用的是HashMap,并没有自身的数据结构,感兴趣可以查看HashMap数据结构; HashMap结构图在1.8中添加了红黑树,内部节点Node源码如下:
      static class Node<K,V> implements Map.Entry<K,V> {
              final int hash;
              final K key;
              V value;
              Node<K,V> next;
      
              Node(int hash, K key, V value, Node<K,V> next) {
                  this.hash = hash;
                  this.key = key;
                  this.value = value;
                  this.next = next;
              }
       }
      
    • 由上述源码可知,每一个节点中有一个指向下一个元素的指针next,所以HashMap的结构图如下所示(图形皆为原创,喜欢请点赞,多多支持):
      HashMap结构图
  • TreeSet:

    • 底层采用树结构来进行存储,内部使用的是TreeMap。
    • 元素按照字符串顺序存储,元素不允许为null。
  • LinkedHashSet:

    • 继承HashSet,内部采用的是LinkedHashMap数据结构
    • 和HashSet一样,内部存储元素不可重复。
    • 和HashSet唯一不同的地方是元素有序
    • 因为内部使用的是LinkedHashMap,它内部的存储元素是LinkedHashMapEntry,源码如下:
    static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
        LinkedHashMapEntry<K,V> before, after;
        LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }
    
    • 我们看到LinkedHashMapEntry继承了Node,并且在Node的基础上多了两个指针,before和afer,这两个指针就是用来记录插入的每一个元素的前后元素位置的,这样就保证了顺序。结构图如下:
      LinkedHashMap

Queue集合队列,不是本节的重点,会在后期单独讲解。

Map集合

  • Map集合中存储的是具有<Key,Value>映射关系的元素,它在工作中是使用最多的数据结构之一。它有几个常用的实现类,HashMap,LinkedHashMap,Hashtable,TreeMap,其实前面讲解Set的时候已经提到了,下面我们再逐个分析一下:

  • HashMap: HashSet中使用的这个数据结构

    • 它通过Key的hashCode()做相关的位运算得到数组中的位置索引,如果hash散列算法以及数组长度合适的话,效率很高。
    • 它可以存入键为null的键值对,JDK1.7是将其存在数组的第一个索引位置。
    • 因为它不是线程安全的,并且在高并发的情况下HashMap容易出现死循环,所以多线程环境下可以使用Collections.synchronizedMap()或者HashTable,它们两个是线程安全的,但是因为HashTable是对方法整体加锁,而Collections.synchronizedMap()是对代码块加锁,所以效率不够高。
    • 在高并发的环境下可以使用ConcurrentHashMap,它采用的是分段加锁机制(1.8中引入了二叉树),HashMap的结构图如下:
      HashMap结构图
  • LinkedHashMap:

    • 它是HashMap的子类,它和HashMap的区别是在HashMap的基础上,增加了一个双向链表来记录存储数据的先后顺序,能够保证遍历数据的时候,首先得到的是先插入的数据。
    • LinkedHashMap的结构图如下:
      LinkedHashMap
  • HashTable:

    • 它不允许存入键值对为null(Key和Value都不能为null,否则会报NullPointerException)。
    • 它是线程安全的,但是因为是对方法整体加锁,所以并发性不太好。一般不使用这个类,在不要求线程安全的时候可以使用HashMap, 对线程安全有要求的时候可以使用ConcurrentHashMap。
    • 数据结构图和HashMap一致。
  • TreeMap

    • 它实现了SortedMap接口,查看put源码可以看到,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。
    • 它能够把保存的记录根据键排序,默认是按键值的升序排序,可以在构造方法中指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。

总结

在工作中根据场景选用合适的集合数据结构,会大大增加程序的运行效率,这就要求得对集合框架非常熟悉才行。本篇中没有对并发和队列的部分做讲解,会在后期单独讲解。

  • 14
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值