(6)Java数据结构-- 转:JAVA常用数据结构及原理分析
JAVA常用数据结构及原理分析 http://www.2cto.com/kf/201506/412305.html
前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balabala讲了一堆,现在总结一下。
java.util包中三个重要的接口及特点:List(列表)、Set(保证集合中元素唯一)、Map(维护多个key-value键值对,保证key唯一)。
其不同子类的实现各有差异,如是否同步(线程安全)、是否有序。
常用类继承树:
以下结合源码讲解常用类实现原理及相互之间的差异。
Collection (所有集合类的接口)
List、Set都继承自Collection接口,查看JDK
API,操作集合常用的方法大部分在该接口中定义了。
Collections
(操作集合的工具类)
对于集合类的操作不得不提到工具类Collections,它提供了许多方便的方法,如求两个集合的差集、并集、拷贝、排序等等。
由于大部分的集合接口实现类都是不同步的,可以使用Collections.synchronized*方法创建同步的集合类对象。
如创建一个同步的List:List
synList = Collections.synchronizedList(new
ArrayList());
其实现原理就是重新封装new出来的对象,操作对象时用关键字synchronized同步。看源码很容易理解。
Collections部分源码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<code class="language-java hljs ">//Collections.synchronizedList返回的是静态类SynchronizedCollection的实例,最终将new出来的ArrayList对象赋值给了Collection<e> c。static class SynchronizedCollection<e> implementsCollection<e>, Serializable { finalCollection<e> c; // Backing Collection finalObject mutex; // Object on which to synchronize SynchronizedCollection(Collection<e> c) { if(c==null) thrownew NullPointerException(); this.c = c; mutex = this; } //... publicboolean add(E e) { //操作集合时简单调用原本的ArrayList对象,只是做了同步 synchronized(mutex) {returnc.add(e);} } //...}</e></e></e></e></e></code> |
List (列表)
ArrayList、Vector是线性表,使用Object数组作为容器去存储数据的,添加了很多方法维护这个数组,使其容量可以动态增长,极大地提升了开发效率。它们明显的区别是ArrayList是非同步的,Vector是同步的。不用考虑多线程时应使用ArrayList来提升效率。
ArrayList、Vector
部分源码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<code class="language-java hljs ">//ArrayList.addpublic boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! //可以看出添加的对象放到elementData数组中去了 elementData[size++] = e; returntrue;}//ArrayList.removepublic E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); intnumMoved = size - index - 1; if(numMoved > 0) //移除元素时数组产生的空位由System.arraycopy方法将其后的所有元素往前移一位,System.arraycopy调用虚拟机提供的本地方法来提升效率 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work returnoldValue;}//Vector add方法上多了synchronized关键字publicsynchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; returntrue;}</code> |
LinkedList是链表,略懂数据结构就知道其实现原理了。链表随机位置插入、删除数据时比线性表快,遍历比线性表慢。
双向链表原理图:
LinkedList部分源码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<code class="language-java hljs ">//源码很清晰地表达了原理图public class LinkedList<e>extends AbstractSequentialList<e>implements List<e>, Deque<e>, Cloneable, java.io.Serializable{ //头尾节点 transientNode<e> first; transientNode<e> last;}//节点类 privatestatic classNode<e> { //节点存储的数据 E item; Node<e> next; Node<e> prev; Node(Node<e> prev, E element, Node<e> next) { this.item = element; this.next = next; this.prev = prev; }}</e></e></e></e></e></e></e></e></e></e></e></code> |
由此可根据实际情况来选择使用ArrayList(非同步、非频繁删除时选择)、Vector(需同步时选择)、LinkedList(频繁在任意位置插入、删除时选择)。
Map(存储键值对,key唯一)
HashMap结构的实现原理是将put进来的key-value封装成一个Entry对象存储到一个Entry数组中,位置(数组下标)由key的哈希值与数组长度计算而来。如果数组当前下标已有值,则将数组当前下标的值指向新添加的Entry对象。
有点晕,看图吧:
看完图再看源码,非常清晰,都不需要注释。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<code class="language-java hljs ">publicclass HashMap<k,v>extends AbstractMap<k,v>implements Map<k,v>, Cloneable, Serializable{ transientEntry<k,v>[] table; publicV put(K key, V value) { if(key == null) returnputForNullKey(value); inthash = hash(key); inti = indexFor(hash, table.length); //遍历当前下标的Entry对象链,如果key已存在则替换 for(Entry<k,v> e = table[i]; e != null; e = e.next) { Object k; if(e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); returnoldValue; } } addEntry(hash, key, value, i); returnnull; }}staticclass Entry<k,v> implementsMap.Entry<k,v> { finalK key; V value; Entry<k,v> next; inthash;}</k,v></k,v></k,v></k,v></k,v></k,v></k,v></k,v></code> |
TreeMap是由Entry对象为节点组成的一颗红黑树,put到TreeMap的数据默认按key的自然顺序排序,new
TreeMap时传入Comparator自定义排序。红黑树网上很多资料,我讲不清,这里就不介绍了。
Set(保证容器内元素唯一性)
之所以先讲Map是因为Set结构其实就是维护一个Map来存储数据的,利用Map结构key值唯一性。
HashSet部分源码:
|
1
2
3
4
5
6
7
8
9
10
11
|
<code class="language-java hljs ">publicclass HashSet<e>extends AbstractSet<e>implements Set<e>, Cloneable, java.io.Serializable{ //无意义对象来作为Map的value privatestatic finalObject PRESENT = newObject(); publicboolean add(E e) { returnmap.put(e, PRESENT)==null; }}</e></e></e></code> |
HashSet、TreeSet分别默认维护一个HashMap、TreeMap。
(6)Java数据结构-- 转:JAVA常用数据结构及原理分析的更多相关文章
- java 中几种常用数据结构
Java中有几种常用的数据结构,主要分为Collection和map两个主要接口(接口只提供方法,并不提供实现),而程序中最终使用的数据结构是继承自这些接口的数据结构类. 一.几个常用类的区别 1.A ...
- Java 集合框架(常用数据结构)
早在Java 2中之前,Java就提供了特设类.比如:向量(Vector).栈(Stack).字典(Dictionary).哈希表(Hashtable)这些类(数据结构)用来存储和操作对象组.虽然这些 ...
- java基础进阶二:HashMap实现原理分析
HashMap实现原理分析 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二 ...
- Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析
1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...
- 【Java编程实战】Metasploit_Java后门运行原理分析以及实现源码级免杀与JRE精简化
QQ:3496925334 文章作者:MG1937 CNBLOG博客ID:ALDYS4 未经许可,禁止转载 某日午睡,迷迷糊糊梦到Metasploit里有个Java平台的远控载荷,梦醒后,打开虚拟机, ...
- 【Java线程安全】 — 常用数据结构及原理(未完结)
本文主要记录自己对于多线程安全的学习,先来记几个线程安全模型. 首先最重要的当然是volatile和AQS了: 我们知道,整个java.cuncurrent包的核心就是volatile,CAS加自旋悲 ...
- 数据结构与算法——常用数据结构及其Java实现
前言 仿佛一下子,2017年就快过去一半了,研一马上就要成为过去式了,我打算抓住研一的尾巴,好好梳理一下数据结构与算法,毕竟这些基础知识是很重要的嘛.所以准备在这里搞一个系列的文章,以期透彻. 本系列 ...
- 【Java并发编程】1、ConcurrentHashMap原理分析
集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构的支持.比如两个线程需要同时访问一个中间临界区(Queue),比如常会用缓存作为外部文件的副本(HashMap).这篇文章主 ...
- Java JDK 动态代理使用及实现原理分析
转载:http://blog.csdn.net/jiankunking 一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理 ...
- 【Java并发编程】23、ConcurrentHashMap原理分析(1.7和1.8版本对比)
jdk 1.8版本 ConcurrentHashMap在1.8中的实现,相比于1.7的版本基本上全部都变掉了.首先,取消了Segment分段锁的数据结构,取而代之的是数组+链表(红黑树)的结构.而对于 ...
随机推荐
- C语言进阶--Day2
今天主要讲解的是函数的压栈与出栈 1. 要实现一个数组的逆置,用栈的压栈出栈观点: reverseArr(int *parr,int i,int len) { if(i != len-1) rever ...
- macs安卓工程创建
第一个 1.创建工程后先修改xml文件. 然后主程序中编写代码. center :如果图片比imageview大则显示中片中心部分 image大小 把view填满 匹配父控件 ,父控件多大它就多大. ...
- (数学) PTA 1005 继续(3n+1)猜想 (25 分)
1005 继续(3n+1)猜想 (25 分) 卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程 ...
- flask学习之路
目录 flask初识 flask模板 flask的session flask路由 flask配置和实例化传参 flask蓝图 更新中
- docker基础之镜像
获取镜像 从 Docker Registry 获取镜像的命令是 docker pull.其命令格式为: docker pull [选项] [Docker Registry地址]<仓库名>: ...
- JAVA核心技术I---JAVA基础知识(二进制文件读写和zip文件读写)
一:二进制文件读写 (一)写文件 –先创建文件,写入数据,关闭文件 –FileOutputStream, BufferedOutputStream,DataOutputStream –DataOutp ...
- HTML5 accesskey的用法
<button onclick="start()" accesskey="s">开始</button> <button oncli ...
- Hadoop记录-监控几个思路
1.存活监控 基本监控,主要对进程的存活.端口连通性.url可检测性等指标进行监控. 2.2 可用性监控 主要指对用户而言是否可用,能否返回预期结果,通常部署在一些业务主流程或一些关键环节,如接口调用 ...
- Linux下常用配置文件
/etc/sysconfig/network 包括主机基本网络信息,用于系统启动 /etc/sysconfig/network-script/ 此目录下是系统启动最初始化网络的信息 /etc/sysc ...
- ACM-ICPC 2018 南京赛区网络预赛 E AC Challenge(状压dp)
https://nanti.jisuanke.com/t/30994 题意 给你n个题目,对于每个题目,在做这个题目之前,规定了必须先做哪几个题目,第t个做的题目i得分是t×ai+bi问最终的最大得分 ...