Java容器-ArrayList、LinkedList、HashMap

ArrayList

        ArrayList List的一个实现类,查询效率比较好,因为它底层是依据数组实现,顺序存储,可以直接通过下标位置定位到某一个元素。但它的增加删除在某些场景下可能比较慢,比如在集合的头部新增或删除一个元素,那么所有旧的元素的位置都会发生变化,如果加上扩容导致数据拷贝,而数据量较多,那么效率会很低.
        ArrayList 初始容量默认为10,该容量是指用来存储列表元素的数组的大小。随着向ArrayList 中不断添加元素, 其容量也自动扩容。扩容会将老数据拷贝到新数组,它的一个扩容是在原来基 础 上 扩大 1.5 倍 (int newCapacity = oldCapacity +(oldCapacity >>1))。如果可预知数据量的多少, 最好在构造ArrayList实例时指定其容量,防止数据量较大导致较多次的扩容影响效率。 

1d65ef97d69342b7b279d645773c58ea.png

LinkedList

        LinkedList也是List的一个实现类,它的增删效率比较高,因为它是节点存储,双向链表实现,每个节点都记录了上一个节点和下一个节点的地址,针对于增加或修改只需要修改前后节点就可以,所以效率比较高. 对于查询在某些场景下效率会比较差,比如恰好要查询最后一个元素,那么就需要从头遍历到最后才能找到。

        LinkedList 中有一个私有内部类Node,是实现LinkedList的关键。它有三个属性,分别是前驱节点(prev),当前节点(E),后继节点(next)。基于这种双向链表结构,LinkedList中提供了很多专门针对头尾元素操作的方法。

c4bab49ba8354acca0b0d5d6d8fd863c.png

HashMap

         HashMap是Map接口的一个非线程安全的实现类, 初始容量为16(DEFAULT_INITIAL_CAPACITY = 1 << 4),还有一个默认的负载因子0.75(DEFAULT_LOAD_FACTOR=0.75f) ,负载因子是查询性能(时间)和内存空间成本上的一种折衷。JDK1.8开始HashMap是由数组+链表+红黑树来实现,1.7则只是数组+链表来实现,红黑树的加入提高了HashMap的查询效率和空间利用率。
        HashMap 通过对key值进行hash计算(扰动函数)后的hash值来快速的存取元素。它将键值对存储在一个数组中,数组中每个元素都是一个链表的头节点。当我们插入一个键值对时,HashMap 首先根据键的哈希值计算出其应该存储在数组的哪个位置,然后将其插入到该位置对应链表的末尾。当我们查找一个键值对时,HashMap 首先根据键的哈希值找到其应该存储的链表,然后在该链表中查找对应的键值对。

        当新增元素时,hashMap判断新增元素后的数组长度大于当前数组阈值(当前数组长度*0.75),hashMap会发生扩容(扩容2倍),而当数组长度>64且当前位置链表长度>8时,链表就会转化为红黑树;而删除元素时,当前位置链表长度<=6时, 红黑树会退化为链表。

//默认的初始容量,必须是2的幂,也就是16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

//最大容量,如果构造函数中指定了一个更高的值,则使用这个值。必须是2的幂且不超过1<<30
static final int MAXIMUM_CAPACITY = 1 << 30;

//默认的负载因子,如果构造函数中没有指定,则使用这个值(经过计算得出的0.75)。
//负载因子是一个浮点数,表示哈希表允许填充的程度。一般来说,负载因子越小,冲突越少,性能越好,但空间利用率越低。
static final float DEFAULT_LOAD_FACTOR = 0.75f;

//将链表转化为红黑树的阈值
//当一个桶(bin)中的节点数达到或超过这个值时,就会将链表转化为红黑树,以提高查找效率
//这个值必须大于2,并且至少为8,以便与删除操作中关于转化回链表的假设相匹配
static final int TREEIFY_THRESHOLD = 8;

//在调整大小(resize)操作中将红黑树转化回链表的阈值。应该小于TREEIFY_THRESHOLD,并且最多为6,以便与删除操作中关于缩小检测相匹配。
static final int UNTREEIFY_THRESHOLD = 6;

//可以进行树化(treeify)的最小表容量。如果桶中节点数太多而表容量太小,则会调整表大小而不是进行树化。应该至少为4 * TREEIFY_THRESHOLD,以避免调整大小和树化阈值之间的冲突。
static final int MIN_TREEIFY_CAPACITY = 64;


 /**
     Node数组,用来存储键值对,Node是一个内部类,表示一个链表节点或者一个红黑树节点。
     table在第一次使用时初始化,并且在需要时进行扩容。table的长度总是2的幂次方,这样可以提高哈希值和数组索引之间的映射效率
     */
    transient Node<K,V>[] table;

    /**
     Set集合,用来缓存hashmap中所有的键值对。entrySet在第一次调用时创建,并且在hashmap结构发生变化时更新。
     entrySet可以用来遍历hashmap中的所有元素。
     */
    transient Set<Map.Entry<K,V>> entrySet;

    /**
     * 包含的键值对的数量
     */
    transient int size;

    /**
     hashmap结构修改的次数。结构修改指的是改变了键值对数量或者内部结构(例如rehash)的操作。
     modCount用来实现快速失败机制,即当迭代器遍历hashmap时,如果发现modCount发生了变化,
     就抛出ConcurrentModificationException异常。
     */
    transient int modCount;

    /**
     扩容的阈值,等于容量乘以负载因子。当size超过threshold时,就会触发resize()方法进行扩容。
     如果table还没有分配空间,那么threshold就表示初始容量,默认为16。
     */
    int threshold;

    /**
     负载因子。负载因子越小,哈希冲突越少,但空间利用率越低;
     负载因子越大,哈希冲突越多,但空间利用率越高。
     loadFactor默认为0.75,在创建hashmap时可以指定。
     */
    final float loadFactor;

53f260e31af642afaedd7e479f115c67.png

注:图片引自Java 8系列之重新认识HashMap - 美团技术团队 (meituan.com)

源码详解: 万字图文——HashMap源码解析(包含红黑树)_红黑树的转化是在插入元素之前还是之后-CSDN博客

 

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值