原文:http://www.javastack.cn/article/2018/data-structure-and-the-principle-diagram/

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

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

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

LinkedList

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

add(E) / addLast(E)

add(index, E)

这边有个小的优化, 他会先判断index是靠近队头还是队尾, 来确定从哪个方向遍历链入.

1         if (index < (size >> 1)) {2             Node<E> x = first;
3             for (int i = 0; i < index; i++)
4                 x = x.next;
5             return x;
6         } else {7             Node<E> x = last;
8             for (int i = size - 1; i > index; i--)
9                 x = x.prev;
10             return x;
11         }

靠队尾

get(index)

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

remove(E)

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

add(index, E)

扩容

一般默认容量是10, 扩容后, 会length*1.5.

remove(E)

循环遍历数组, 判断E是否equals当前元素, 删除性能不如LinkedList.

Stack
经典的数据结构, 底层也是数组, 继承自Vector, 先进后出FILO, 默认new Stack()容量为10, 超出自动扩容.

push(E)

pop()

后缀表达式
Stack的一个典型应用就是计算表达式如 9 + (3 - 1) * 3 + 10 / 2, 计算机将中缀表达式转为后缀表达式, 再对后缀表达式进行计算。推荐阅读:Java 程序员必须掌握的 8 道数据结构面试题,你会几道?

中缀转后缀

数字直接输出

栈为空时,遇到运算符,直接入栈

遇到左括号, 将其入栈

遇到右括号, 执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。

遇到运算符(加减乘除):弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈

最终将栈中的元素依次出栈,输出。

计算后缀表达

遇到数字时,将数字压入堆栈

遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算, 并将结果入栈

重复上述过程直到表达式最右端

运算得出的值即为表达式的结果

队列
与Stack的区别在于, Stack的删除与添加都在队尾进行, 而Queue删除在队头, 添加在队尾.

ArrayBlockingQueue

生产消费者中常用的阻塞有界队列, FIFO.

put(E)

put(E) 队列满了

1 final ReentrantLock lock = this.lock;
2 lock.lockInterruptibly();
3 try {
4 while (count == items.length)
5 notFull.await();
6 enqueue(e);
7 } finally {
8 lock.unlock();
9 }

take()

当元素被取出后, 并没有对数组后面的元素位移, 而是更新takeIndex来指向下一个元素.

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

1 private E dequeue() {
2 // assert lock.getHoldCount() == 1;
3 // assert items[takeIndex] != null;
4 final Object[] items = this.items;
5 @SuppressWarnings(“unchecked”)
6 E x = (E) items[takeIndex];
7 items[takeIndex] = null;
8 if (++takeIndex == items.length)
9 takeIndex = 0;
10 count–;
11 if (itrs != null)
12 itrs.elementDequeued();
13 notFull.signal();
14 return x;
15 }

HashMap
最常用的哈希表, 面试的童鞋必备知识了, 内部通过数组 + 单链表的方式实现. jdk8中引入了红黑树对长度 > 8的链表进行优化, 我们另外篇幅再讲。推荐阅读:Java 程序员必须掌握的 8 道数据结构面试题,你会几道?

put(K, V**)**

put(K, V) 相同hash值

resize 动态扩容

当map中元素超出设定的阈值后, 会进行resize (length * 2)操作, 扩容过程中对元素一通操作, 并放置到新的位置。推荐阅读:Java 程序员必须掌握的 8 道数据结构面试题,你会几道?

具体操作如下:

在jdk7中对所有元素直接rehash, 并放到新的位置.

在jdk8中判断元素原hash值新增的bit位是0还是1, 0则索引不变, 1则索引变成”原索引 + oldTable.length”.

1 //定义两条链
2 //原来的hash值新增的bit为0的链,头部和尾部
3 Node<K,V> loHead = null, loTail = null;
4 //原来的hash值新增的bit为1的链,头部和尾部
5 Node<K,V> hiHead = null, hiTail = null;
6 Node<K,V> next;
7 //循环遍历出链条链
8 do {
9 next = e.next;
10 if ((e.hash & oldCap) == 0) {
11 if (loTail == null)
12 loHead = e;
13 else
14 loTail.next = e;
15 loTail = e;
16 }
17 else {
18 if (hiTail == null)
19 hiHead = e;
20 else
21 hiTail.next = e;
22 hiTail = e;
23 }
24 } while ((e = next) != null);
25 //扩容前后位置不变的链
26 if (loTail != null) {
27 loTail.next = null;
28 newTab[j] = loHead;
29 }
30 //扩容后位置加上原数组长度的链
31 if (hiTail != null) {
32 hiTail.next = null;
33 newTab[j + oldCap] = hiHead;
34 }

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

put(K, V)

get(K)

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

accessOrder为true的时候, 需要将最近访问的元素, 放置到队尾.

removeEldestEntry 删除最老的元素

(完)

【Java】图解 Java 中的数据结构及原理相关推荐

  1. 图解 Java 中的数据结构及原理,傻瓜也能看懂!

    最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList Linke ...

  2. 图解java_图解 Java 中的数据结构及原理!

    作者:大道方圆 cnblogs.com/xdecode/p/9321848.html 最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于 ...

  3. 【Java基础】HashMap底层数据结构及其原理

    1.简单了解一下HashMap HashMap 就是以 Key-Value 键值对的方式进行数据存储的一种数据结构,它在 JDK 1.7 和 JDK 1.8 中底层数据结构是有些不一样的.简单来说,J ...

  4. java postconstruct_spring框架中@PostConstruct的实现原理

    在spring项目经常遇到@PostConstruct注解,首先介绍一下它的用途: 被注解的方法,在对象加载完依赖注入后执行. 此注解是在Java EE5规范中加入的,在Servlet生命周期中有一定 ...

  5. Java并发包中Semaphore的工作原理、源码分析及使用示例

    简介: 在多线程程序设计中有三个同步工具需要我们掌握,分别是Semaphore(信号量),countDownLatch(倒计数门闸锁),CyclicBarrier(可重用栅栏) 欢迎探讨,如有错误敬请 ...

  6. Java集合- HashMap 的底层数据结构实现原理

    一.HashMap 的数据结构 JDK1.8 之前 JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列. HashMap 通过 key 的 hashCode 经过扰 ...

  7. java filter response_ZuulFilter中设置Response的原理。

    在网上看到一个Zuul过滤器异常后,自定义Response的例子如下: RequestContext ctx = RequestContext.getCurrentContext(); ctx.set ...

  8. Java并发包中Lock的实现原理

    https://www.cnblogs.com/nullzx/p/4968674.html https://www.cnblogs.com/zouzz/p/6593748.html 转载于:https ...

  9. mysql join图解_MySQL中Join算法实现原理分析[多图]

    在MySQL 中,只有一种 Join 算法,就是大名鼎鼎的 Nested Loop Join,他没有其他很多数据库所提供的 Hash Join,也没有 Sort Merge Join.顾名思义,Nes ...

最新文章

  1. java链表寻找中间节点
  2. Centos7下删除virbr0网卡信息
  3. 单片机小白学步系列(十三) 点亮第一个LED——好的開始,成功的一半
  4. chkconfig 系统服务管理
  5. npz文件转为npy_numpy的文件存储 .npy .npz 文件
  6. wxpython界面切换_wxpython实现按钮切换界面的方法
  7. javascript实现jsonp跨域问题+原理
  8. linux之RAID知识总结
  9. qt中append函数_Qt 加载cern-root库 并调用root类
  10. 可达龙博客的第一篇文
  11. http 请求 返回状态码 405 的问题
  12. 解决微信公众号accessToken白名单问题
  13. 如何通过笔记本共享网络给台式
  14. GRUB4DOS(十一) grldr可启动的软盘或硬盘分区的说明
  15. wifi爆破学习记录
  16. 点到线段的最短距离算法
  17. 机器人中的坐标转换关系(个人记录学习)
  18. Vue基础实战-敲击瓶子
  19. 服务器c盘清理文件,云服务器c盘满了怎么清理
  20. networkx学习(五)无标度网络

热门文章

  1. 三星Galaxy S21 FE官网“偷跑”:延续高性价比策略
  2. 纽交所决定将蛋壳公寓ADS摘牌
  3. 京东方OLED屏幕无缘iPhone 12首批供货,因测试未通过
  4. 阿里第三财季:下沉市场再立功 疫情之下祸福相依
  5. iPhone 11办理联通5G套餐后,上网速度变快?网友:发广告翻车了?
  6. 双11 拼多多iPhone 11系列香气扑鼻:最高降幅达1600元
  7. 股价口碑相继探底 这家“比新东方还要成功”的机构能用AI搏出明天吗?
  8. “球鞋一面墙,堪比一套房” 央视评炒鞋乱象:呼吁“鞋穿不炒”
  9. 中国5G标准专利数量遥遥领先:不卖产品也获利
  10. java中的过滤器:Filter