动图解析:《JAVA中的数据结构及原理》

编程比如是一辆汽车,而数据结构和算法是汽车内部的变速箱。一个开车的人不懂变速箱的原理也是能开车的,同理一个不懂数据结构和算法的人也能编程。可是若是一个开车的人懂变速箱的原理,好比下降速度来得到更大的牵引力,或者经过下降牵引力来得到更快的行驶速度。最近在整理数据结构方面的知识, 系统化看了下Java中经常使用数据结构, 突发奇想用动态图来绘制数据流转过程。本篇文章主要基于jdk8, 可能会有些特性与jdk7之间不相同,jdk8与jdk7不一样之处请看下图:java


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


LinkedList算法

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

add(E) / addLast(E)数组

add(index, E)缓存

这边有个小的优化, 他会先判断index是靠近队头仍是队尾, 来肯定从哪一个方向遍历链入:数据结构



靠队尾数据结构和算法


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, 计算机将中缀表达式转为后缀表达式, 再对后缀表达式进行计算。

中缀转后缀

数字直接输出

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

遇到左括号, 将其入栈

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

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

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


计算后缀表达

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

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

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

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


队列

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

ArrayBlockingQueue

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

put(E)


put(E) 队列满了



take()

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

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



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".



LinkedHashMap

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

put(K, V)


get(K)

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

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


removeEldestEntry 删除最老的元素


以上就是java数据结构及原理的所有内容,但愿你们对你们有所帮助,

相关文章
相关标签/搜索