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> implements Collection<e>, Serializable {
final Collection<e> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<e> c) {
if (c== null )
throw new NullPointerException();
this .c = c;
mutex = this ;
}
//...
public boolean add(E e) {
//操作集合时简单调用原本的ArrayList对象,只是做了同步
synchronized (mutex) { return c.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.add
public boolean add(E e) {
ensureCapacityInternal(size + 1 ); // Increments modCount!!
//可以看出添加的对象放到elementData数组中去了
elementData[size++] = e;
return true ;
} //ArrayList.remove public E remove( int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = 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
return oldValue;
} //Vector add方法上多了synchronized关键字 public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1 );
elementData[elementCount++] = e;
return true ;
}</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
{ //头尾节点
transient Node<e> first;
transient Node<e> last;
} //节点类 private static class Node<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 " > public class HashMap<k,v>
extends AbstractMap<k,v>
implements Map<k,v>, Cloneable, Serializable
{ transient Entry<k,v>[] table;
public V put(K key, V value) {
if (key == null )
return putForNullKey(value);
int hash = hash(key);
int i = 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 );
return oldValue;
}
}
addEntry(hash, key, value, i);
return null ;
}
} static class Entry<k,v> implements Map.Entry<k,v> {
final K key;
V value;
Entry<k,v> next;
int hash;
}</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 " > public class HashSet<e>
extends AbstractSet<e>
implements Set<e>, Cloneable, java.io.Serializable
{ //无意义对象来作为Map的value
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.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分段锁的数据结构,取而代之的是数组+链表(红黑树)的结构.而对于 ...
随机推荐
-
java 实现死锁
package 线程安全的讨论; class DThread implements Runnable { private Object o1=null; private Object o2=null; ...
-
20160329javaweb之JSP -cookie入门
一.什么是会话? •会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 会话过程中要解决的一些问题? •每个用户在使用浏览器与服务器 ...
-
RVCT的Linux环境变量配置 ARM&#174; RVDS™ 4.1(b713)
下载解压 armrvds.tar.gz到/opt 下 在自己的build.sh下导入RVCT的环境变量配置ARM® RVDS™4.1(b713): export ARMROOT=/opt/armrvd ...
-
poj 1664 把平果
这个问题可分为两个子问题:什么时候m<n时刻,例如3苹果放在4阿菜,和3苹果放3一样的. 所以m<n时,f[m][n]=f[m][m]; 当m>=n时.可分为两种放法,一种为至少有一 ...
-
[leetcode-624-Maximum Distance in Arrays]
Given m arrays, and each array is sorted in ascending order. Now you can pick up two integers from t ...
-
JDBC数据库编程
常识名词:ODBC ,JDBC,JDBC API ,JDBC Driver API 数据准备,续上节: JDBC编程流程 最基本的JDBC操作 本段内容主要完成JDBC的增删查改操作 packa ...
-
分布式代码管理系统GIT
1.1Git安装 CentOS上 yum install -y epel-release; yum install git Ubuntu上 apt-get install git Windo ...
-
js 获取DOM的style属性
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
-
python循环字符转换
pyhon函数传参的时候穿的是引用,而不是实际值,这样可以节省内存 变量名要求:最好是以字母下划线作为变量名,不能和py关键字重复 import getpass提供了平台无关的在命令行下输入密码的方法 ...
-
Activity服务类-6 ManagementService服务类
一共含有17个方法 // 获取包含了Activiti数据库模式的{表名.行计数}项的映射.Map<String, Long> getTableCount();//获取诸如任务.执行之类的A ...