浅谈 Java 集合 | 底层源码解析

在 Java 中,我们经常会使用到一些处理缓存数据的集合类,这些集合类都有自己的特点,今天主要分享下 Java 集合中几种经常用的 Map、List、Set。

目录

1、Map

一、背景

二、Map家族

三、HashMap、Hashtable等

四、HashMap 底层数据结构

2、List

一、List 包括的子类

二、ArrayList

三、ArrayList 源码分析

四、LinkedList

五、LinkedList 源码分析

3、Set

一、Set的实质

二、HashSet

三、TreeSet

01

集合 1:Map

背景

如果一个海量的数据中,需要查询某个指定的信息,这时候,可能会犹如大海捞针,这时候,可以使用 Map 来进行一个获取。因为 Map 是键值对集合。Map这种键值(key-value)映射表的数据结构,作用就是通过key能够高效、快速查找value。

举一个例子:

import java.util.HashMap;
import java.util.Map;
import java.lang.Object;


public class Test {
    public static void main(String[] args) {
        Object o = new Object();
        Map<String, Object> map = new HashMap<>();
        map.put("aaa", o); //将"aaa"和 Object实例映射并关联
        Student target = map.get("aaa"); //通过key查找并返回映射的Obj实例
        System.out.println(target == o); //true,同一个实例
        Student another = map.get("bbb"); //通过另一个key查找
        System.out.println(another); //未找到则返回null
    }
}

Map<K, V>是一种键-值映射表,当我们调用put(K key, V value)方法时,就把key和value做了映射并放入Map。当我们调用V get(K key)时,就可以通过key获取到对应的value。如果key不存在,则返回null。和List类似,Map也是一个接口,最常用的实现类是HashMap。

在 Map<K, V> 中,如果遍历的时候,其 key 是无序的,如何理解:

import java.util.HashMap;
import java.util.Map;
public class Test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("dog", "a");
        map.put("pig", "b");
        map.put("cat", "c");
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }
    }
}


//print
cat = c
dog = a
pig = b
从上面的打印结果来看,其是无序的,有序的答案可以在下面找到。

接下来我们分析下 Map ,首先我们先看看 Map 家族:


它的子孙下面有我们常用的 HashMap、LinkedHashMap,也有 TreeMap,另外还有继承 Dictionary、实现 Map 接口的 Hashtable。

下面针对各个实现类的特点来说明:

(1)HashMap:它根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有高效的访问速度,但遍历顺序却是不确定的。HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap 非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的静态方法 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap(分段加锁)。

(2)LinkedHashMap:LinkedHashMap 是 HashMap 的一个子类,替 HashMap 完成了输入顺序的记录功能,所以要想实现像输出同输入顺序一致,应该使用 LinkedHashMap。

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

(4)Hashtable:Hashtable继承 Dictionary 类,实现 Map 接口,很多映射的常用功能与 HashMap 类似,Hashtable 采用"拉链法"实现哈希表,不同的是它来自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,但并发性不如 ConcurrentHashMap,因为ConcurrentHashMap 引入了分段锁。Hashtable 使用 synchronized 来保证线程安全,在线程竞争激烈的情况下 HashTable 的效率非常低下。不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换。Hashtable 并不是像 ConcurrentHashMap 对数组的每个位置加锁,而是对操作加锁,性能较差。 

上面讲到了 HashMap、Hashtable、ConcurrentHashMap,接下来先看看 HashMap 的源码实现:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {


    private static final long serialVersionUID = 362498820763181265L;
    /**
      * 默认大小 16
      */
      static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
      
      /**
      * 最大容量是必须是2的幂30
      */
      static final int MAXIMUM_CAPACITY = 1 << 30;
      
      /**
      * 负载因子默认为0.75,hashmap每次扩容为原hashmap的2倍
      */
      static final float DEFAULT_LOAD_FACTOR = 0.75f;
      
      /**
      * 链表的最大长度为8,当超过8时会将链表装换为红黑树进行存储
      */
      static final int TREEIFY_THRESHOLD = 8;
      
      /**
       * The table, initialized on first use, and resized as
       * necessary. When allocated, length is always a power of two.
       * (We also tolerate length zero in some operations to allow
       * bootstrapping mechanics that are currently not needed.)
       */
      transient Node<K,V>[] table;
      
      static class Node&l
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值