Java数据结构分析

本文深入分析了Java中几种常见的数据结构,包括ArrayList、LinkedList、Vector、HashMap以及ArrayMap等。讨论了它们的内部实现、查找、插入和删除的效率,以及线程安全性。例如,ArrayList依赖于数组,插入和删除操作效率较低但查找快速;HashMap使用数组+链表,通过哈希函数定位元素,但在高冲突时可能导致链表过长。同时,文章提到了线程安全的Vector和适用于特定场景的SparseArray及TreeMap。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

transient 这个关键字保证其修饰的数据不被序列化

ArrayList

内部保存了一个数组  transient Object[] elementData; 初识容量为10,用来存储数据,所以在内存分配上需要连续的存储空间,

查找: get(int index) 通过elementData收地址 + (index * 每个元素所占的空间大小)即可得到要查找元素的地址,所以效率很快

插入和删除: 内部时通过数组实现的,所以插入和删除操作, 先从头遍历找到需要插入或删除的位置,然后通过移动,拷贝数组元素,这种效率狠低

插入时,当数组慢了的时候,会有扩容操作,扩容后的容量变为之前容量的1.5呗

线程安全: 否

LinkedList

内部通过单链表的形式实现 

transient Node<E> first; //头指针

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last; // 尾指针

查找:每次都需要从头开始遍历,计数,效率慢

插入 和 删除: 先从头遍历找到需要插入或删除的位置,然后修改前后指针即可,不涉及到数组拷贝等操作,效率高

插入时 无需扩容

线程安全: 否

Vector 

同ArrayList的存储,读写 几乎完全一样

int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                 capacityIncrement : oldCapacity);
// 扩容时后的长度,如果指定capacityIncrement,则oldCapacity+capacityIncrement,否则变为2倍

唯一的区别时所有的操作都加锁了,一定程度上保证了线程安全,但是每个方法都加了Synchronized,会影响一些并发效率

 

Map系列

HashMap,Treemap,ArrayMap,ConcurrentHashMap

HashMap 

内部也是采用了一个数组 + 链表的形式,数组中的元素为Map.Entry<K,V>组成的链表,体现为key,value的组合,默认初始容量为16,默认loadFactor 为0.75

查找index的过程: 对hashcode % (length -1);

put 操作

判断hash(key)查找在数组中的index,如果index中有数据,沿着对应的链表中查找是否有相应的key,如果有直接修改value

如果链表中没有,则在链表末尾创建新的entry<key,value>

如果当前数组中的元素数量到达loadFactor * capacity时,触发扩容操作,扩容后,容量变为变为2倍

为什么扩容? 如果不扩容,会导致每个index中的链表越来越长,降低了查找效率

扩容过程:

1 创建一个长度为旧长度的2倍

2 旧数据迁移到新数组中

  

do {
    next = e.next;
    //  在链表中的偶数index保存中低index的数据
    if ((e.hash & oldCap) == 0) { 
        if (loTail == null)
            loHead = e;
        else
            loTail.next = e;
        loTail = e;
    }
    // 在链表中的偶数index 保存在高index的数据
    else {    
        if (hiTail == null)
            hiHead = e;
        else
            hiTail.next = e;
        hiTail = e;
    } while ((e = next) != null);
      if (loTail != null) {
         //低index
         loTail.next = null;
         newTab[j] = loHead;
        }
      if (hiTail != null) {
         // 高index
          hiTail.next = null;
          newTab[j + oldCap] = hiHead;
      }
} 

迁移数据后,将之前所有的链表一份为2,部分存在低index,部分存在高index

线程安全:否

关于为什么hashmap的长度是2的n次方,是因为再计算table的index时,hash % length 等同于 (hash & length -1) ,后者的按位与效率更高

https://blog.youkuaiyun.com/sybnfkn040601/article/details/73194613

ArrayMap 

存储结构为2个数组 

int[] mHashes;
// 有序数组(包含重复属于,因为存在多个key对应同一个hashcode的情况),存储所有key的hash,用于查找key的index
Object[] mArray; //存储了key,value,如key的pos2,则mArray[2]为key,mArray[3]为value,key,value在数组中连续存储

查找key的index过程就是在mHashes中通过二分查找hash(key)的index,

然后key = mArray[index<<1] ,value =  mArray[(index<<1)+1] ;

put操作:

涉及到index的查找,两个数组都会涉及到数组拷贝,还有扩容操作,基本上扩容长度变为1.5倍,扩容后,迁移旧数据到新数组中

线程安全: 否

SparseArray:

与ArrayMap类似,采用两个数组,key和value在两个数组中的index一致,两个数组长度一致,

private int[] mKeys; // key都是int的,
private Object[] mValues;// values

  查找index也是通过二分查找(时间复杂度O(logn))进行,也存在扩容,扩容后长度变为2倍,扩容后的数据迁移

 

HashMap,ArrayMap,SparseArray 比较参考: https://www.jianshu.com/p/679ea6534bc0

ArrayMap,SparseArray相对HashMap来说只是减少了对象的创建,如HashMap中需要创建Entry<key,value>,查找效率不HashMap,

SparseArray中储存的key是int的,所以减少了hash操作,效率更高一点

TreeMap:

TreeMap的设计目的是将所有的数据,按照key进行排序,

内部采用了红黑树的结构,其实本质上就是一个链表,每个节点的数据结构如下: 

static final class TreeMapEntry<K,V> implements Map.Entry<K,V> {
    K key;
    V value;
    TreeMapEntry<K,V> left; //左孩子
    TreeMapEntry<K,V> right;// 右孩子
    TreeMapEntry<K,V> parent;// 父节点,如果为空,则其为树的根节点

    boolean color = BLACK;
}

线程安全:否

TreeMap参考文档 https://blog.youkuaiyun.com/SEU_Calvin/article/details/52746000

 

List,Map在通过for循环遍历过程中中,删除元素,会出现一个ConcurrentModificationException,其实就是内部在通过for循环遍历时,存在判断modCount != exceptedModCount,导致

concurrenthashmap: https://www.cnblogs.com/ITtangtang/p/3948786.html

https://juejin.im/post/5ca89afa5188257e1d4576ff#heading-5

concurrent在java8实现类似与之前的hashmap,只是和java7一样,采用了分段锁概念,插入和删除数据时,使用tab[index]的链表头作为锁,内部采用了大量的cas原理,来保证来更新数据。初识容量16,加载因子0.75,包括扩容操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值