转载:图解Java常用数据结构(一)

本文深入解析Java中LinkedList、ArrayList、HashMap及LinkedHashMap等数据结构的实现原理,涵盖链表、数组、哈希表和红黑树等核心概念,详细阐述了各种数据结构的优缺点及其应用场景。

搬运:https://www.cnblogs.com/xdecode/p/9321848.html

图解Java常用数据结构(一)

最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程.

主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList LinkedHashMap中的双向列表不再是回环的.

HashMap中的单链表是尾插, 而不是头插入等等, 后文不再赘叙这些差异, 本文目录结构如下:
在这里插入图片描述

LinkedList

经典的双链表结构, 适用于乱序插入, 删除. 指定序列操作则性能不如ArrayList, 这也是其数据结构决定的.

add(E) / addLast(E)
在这里插入图片描述
add(index, E)
这边有个小的优化, 他会先判断index是靠近队头还是队尾, 来确定从哪个方向遍历链入.

if (index < (size >> 1)) {
    Node<E> x = first;
    for (int i = 0; i < index; i++)
        x = x.next;
    return x;
} else {
    Node<E> x = last;
    for (int i = size - 1; i > index; i--)
        x = x.prev;
   return x;
}

靠对头
在这里插入图片描述
靠队尾
在这里插入图片描述
get(index)

也是会先判断index, 不过性能依然不好, 这也是为什么不推荐用for(int i = 0; i < lengh; i++)的方式遍历linkedlist, 而是使用iterator的方式遍历.
在这里插入图片描述

在这里插入图片描述
remove(E)
在这里插入图片描述

ArrayList

底层就是一个数组, 因此按序查找快, 乱序插入, 删除因为涉及到后面元素移位所以性能慢.

add(index, E)

在这里插入图片描述
扩容
一般默认容量是10, 扩容后, 会length*1.5.
在这里插入图片描述
remove(E)

final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
	while (count == items.length)
		notFull.await();
		enqueue(e);
	} finally {
		lock.unlock();
}

在这里插入图片描述
take()
当元素被取出后, 并没有对数组后面的元素位移, 而是更新takeIndex来指向下一个元素.

takeIndex是一个环形的增长, 当移动到队列尾部时, 会指向0, 再次循环.

private E dequeue() {
		// assert lock.getHoldCount() == 1;
		// assert items[takeIndex] != null;
		final Object[] items = this.items;
		@SuppressWarnings("unchecked")
		E x = (E) items[takeIndex];
		items[takeIndex] = null;
		if (++takeIndex == items.length)
			takeIndex = 0;
		count--;
		if (itrs != null)
			itrs.elementDequeued();
		notFull.signal();
		return x;
	}

在这里插入图片描述
HashMap
最常用的哈希表, 面试的童鞋必备知识了, 内部通过数组 + 单链表的方式实现. jdk8中引入了红黑树对长度 > 8的链表进行优化, 我们另外篇幅再讲.

put(K, V)
在这里插入图片描述
put(K, V) 相同hash值
在这里插入图片描述
resize 动态扩容
当map中元素超出设定的阈值后, 会进行resize (length * 2)操作, 扩容过程中对元素一通操作, 并放置到新的位置.

具体操作如下:

  • 在jdk7中对所有元素直接rehash, 并放到新的位置.
  • 在jdk8中判断元素原hash值新增的bit位是0还是1, 0则索引不变,
    1则索引变成"原索引 + oldTable.length".
//定义两条链
//原来的hash值新增的bit为0的链,头部和尾部
Node<K,V> loHead = null, loTail = null;
//原来的hash值新增的bit为1的链,头部和尾部
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
//循环遍历出链条链
do {
    next = e.next;
    if ((e.hash & oldCap) == 0) {
        if (loTail == null)
            loHead = e;
        else
            loTail.next = e;
        loTail = e;
    }
    else {
        if (hiTail == null)
            hiHead = e;
        else
            hiTail.next = e;
        hiTail = e;
    }
} while ((e = next) != null);
//扩容前后位置不变的链
if (loTail != null) {
    loTail.next = null;
    newTab[j] = loHead;
}
//扩容后位置加上原数组长度的链
if (hiTail != null) {
    hiTail.next = null;
    newTab[j + oldCap] = hiHead;
}

在这里插入图片描述

LinkedHashMap

继承自HashMap, 底层额外维护了一个双向链表来维持数据有序. 可以通过设置accessOrder来实现FIFO(插入有序)或者LRU(访问有序)缓存.

put(K, V)
在这里插入图片描述
get(K)

accessOrder为false的时候, 直接返回元素就行了, 不需要调整位置.

accessOrder为true的时候, 需要将最近访问的元素, 放置到队尾.
在这里插入图片描述
removeEldestEntry 删除最老的元素
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值