总体探究Java集合

Posted zhengqi-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了总体探究Java集合相关的知识,希望对你有一定的参考价值。

  集合就是将有共同特点的事物放在一起,形成一个整体,方便对他们进行批量处理。Java集合就是将同一种数据结构的数据放在一个容器里,形成一个大对象,方便对这个容器里的同类型(泛型。不是指同一个类的对象)的对象进行遍历等操作,主要分为两大类:Collection 和 Map。Collection是数组结构或链表结构;Map是键值对结构。下面的内容我们逐个从每种集合的数据结构、实现原理、线程安全等方面开始学习。

  集合框架图:

    技术图片

 

一、 Collection 

  Collection是Java对单列集合的抽象,是一个接口,它下面有List、Set、Queue等子接口。

  List集合:List接口是对有序集合的抽象,存入其中的对象可以重复。List接口的实现类有的通过数组结构实现,有下标(如:ArrayList、Vector);有的通过链表结构实现,没有下标(如:LinkedList)。

    ArrayList:数组结构的集合。查询快(因为有下标可以快速定位到元素),增删慢(因为需要修改受影响元素的下标);线程不安全,

    Vector:线程安全(目前已被ArrayList替代)。

    LinkedList:链表结构的集合。查询慢(因为需要遍历,挨个比较),增删快(因为只需要修改一个受影响元素的指向地址)

  Set集合:Set接口是对无序(存入和取出顺序不一样)集合的抽象,存入其中的对象不可重复。Set接口的实现类常见的有:HashSet(通过hashCode和equals方法来确保元素唯一性的)、TreeSet(通过compareTo方法确保元素的唯一性)。

    HashSet:底层数据结构是哈希表。是线程不安全的。不同步。(实现原理与HashMap相似,见下文HashMap部分)

        HashSet如何保证元素是唯一的?先比较hashCode,如果hashCode相同,再通过equals方法比较,如果都相同,则说明是同一个对象,不允许存入HashSet集合。

      LinkedHashSet:继承了HashSet,底层使用了LinkedHashMap来保存元素,在元素操作上与HashSet相同。

    TreeSet:有序的Set集合,默认为自然排序,可通过创建时提供的Comparator来进行排序。实现方式类似于LinkedHashMap(见下文)。

  Queue集合: Queue接口是队列结构的集合的抽象。Queue的工作原理是FCFS算法(First Come, First Serve)。

 

二、Map

  Map是键值对结构的数据集合的抽象,Map下面常用的有HashMap、TreeMap、HashTable等。

  1、HashMap的实现原理:

  HashMap的底层其实是数组+链表结构,查看HashMap的源码就会发现,HashMap内部有个Node类型的成员数组table,这就是HashMap的数组实现,Node类继承了Entry类,Node中有int hash,key,value,Node next 属性,next属性就是HashMap的链表实现。下面我们以源码为标准,深入理解一下HashMap的设计思想和实现逻辑。

  2、HashMap的属性和方法

  2.1 属性:  

  serialVersionUID  ---- 序列化id
  DEFAULT_INITIAL_CAPACITY ---- 初始容量,容量是哈希表中桶(Entry数组)的数量,必须是2的整数次幂。默认初始容量为16,可以在构造函数public HashMap(int initialCapacity)参数中指定。

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 

  MAXIMUM_CAPACITY ---- 最大容量。2的30次方。
  DEFAULT_LOAD_FACTOR ---- 加载因子。因此初始情况下,当桶中键值对的数量大于 16 * 0.75 = 12 时,就会触发扩容。
  TREEIFY_THRESHOLD ---- 为防止链表长度过长,当长度达到一定值时,转换成红黑树。这个值表示当某个桶中,链表长度大于 8 时,有可能会转化成树。
  UNTREEIFY_THRESHOLD ---- 当发现红黑树长度小于6时,重新转换成链表。
  MIN_TREEIFY_CAPACITY ---- 转变成树之前,会有一次判断,只有键值对数量大于 64 才会发生转换。这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化。
  table ---- Entry数组(桶),用于存放Entry链表/树,不对外提供访问方法。
  entrySet ---- Entry集合,提供entrySet()方法,给外部遍历用。

  size ---- (key-value)键值对的个数
  modCount ---- 字段表示结构上被修改的次数。结构上的修改表示增加或者删除元素。例如在iterator遍历map时,删除一个元素,则会抛出ConcurrentModificationException异常。
  threshold ---- 当hashMap中kv数大于这个数时,执行resize方法,对hashmap进行扩容。threshold=capacity*loadFactor
  loadFactor ---- 加载因子。默认为0.75,在扩容时计算扩容门槛用。比如第一次扩容时,capacity=16,loadFactor=0.75, threshold=16*0.75=12,表示当集合中的kv数大于12时,扩容。

  2.2 方法:

  构造方法:

  HashMap()  -->  HashMap(int initialCapacity) --> HashMap(int initialCapacity, float loadFactor) : 最终调用的都是HashMap(int initialCapacity, float loadFactor)方法,用来指定桶容量和加载因子。

  HashMap(Map<? extends K, ? extends V> m) : 初始化HashMap时,把参数中的map放到新的HashMap中,如果源map的容量大于了扩容门槛,则需要调用resize()方法进行扩容。

  其他方法:

  static final int hash(object):获取Key对象的hashCode。hashCode的作用是减少equals方法的调用次数。

  static final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict): 把一个map放到HashMap中。evict为false表示新初始化一个table,为true则表示往原有的元素后累加。

  public int size(): 获取map中K-V键值对的数量。
  public boolean isEmpty() :  判断该HashMap是否为空。
  public V get(K key) :根据Key获取Value。
  getNode(int hash, Object key): 获取节点。首先用hash找到

  containsKey
  public void  put(K key, V value): 往HashMap中放入元素。首先看table是否为空,如果为空,就需要扩容,执行resize()方法。
  putVal
  resize():初始化或者加倍table的大小。
  treeifyBin
  putAll
  remove
  removeNode
  clear
  containsValue
  keySet
  values
  entrySet
  getOrDefault
  putIfAbsent
  remove
  replace
  replace
  computeIfAbsent
  computeIfPresent
  compute
  merge
  forEach
  replaceAll
  clone
  loadFactor
  capacity
  writeObject
  readObject
  newNode
  replacementNode
  newTreeNode
  replacementTreeNode
  reinitialize
  afterNodeAccess
  afterNodeInsertion
  afterNodeRemoval
  internalWriteEntries

 

以上是关于总体探究Java集合的主要内容,如果未能解决你的问题,请参考以下文章

Java面试准备之探究集合(未完)

Java 集合系列01之 总体框架

Java 集合系列01之 总体框架

Java集合总体框架

Java集合系列---总体框架

Java 集合系列目录(Category)