JAVA经常使用数据结构及原理分析
前不久面试官让我说一下怎么理解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同步。看源代码非常easy理解。
Collections部分源代码:
//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);}
}
//...
}List (列表)
ArrayList、Vector是线性表。使用Object数组作为容器去存储数据的,加入了非常多方法维护这个数组。使其容量能够动态增长。极大地提升了开发效率。它们明显的差别是ArrayList是非同步的。Vector是同步的。不用考虑多线程时应使用ArrayList来提升效率。
ArrayList、Vector 部分源代码:
//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;
}LinkedList是链表,略懂数据结构就知道事实上现原理了。链表随机位置插入、删除数据时比线性表快,遍历比线性表慢。
双向链表原理图:
LinkedList部分源代码://源代码非常清晰地表达了原理图
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;
}
}
由此可依据实际情况来选择使用ArrayList(非同步、非频繁删除时选择)、Vector(需同步时选择)、LinkedList(频繁在任何位置插入、删除时选择)。
Map(存储键值对。key唯一)
HashMap结构的实现原理是将put进来的key-value封装成一个Entry对象存储到一个Entry数组中,位置(数组下标)由key的哈希值与数组长度计算而来。假设数组当前下标已有值,则将数组当前下标的值指向新加入的Entry对象。
有点晕。看图吧:
看完图再看源代码。非常清晰。都不须要凝视。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;
}TreeMap是由Entry对象为节点组成的一颗红黑树,put到TreeMap的数据默认按key的自然顺序排序,new TreeMap时传入Comparator自己定义排序。红黑树网上非常多资料,我讲不清,这里就不介绍了。
Set(保证容器内元素唯一性)
之所以先讲Map是由于Set结构事实上就是维护一个Map来存储数据的。利用Map结构key值唯一性。
HashSet部分源代码: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;
}
}HashSet、TreeSet分别默认维护一个HashMap、TreeMap。
JAVA经常使用数据结构及原理分析的更多相关文章
- JAVA常用数据结构及原理分析
JAVA常用数据结构及原理分析 http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balaba ...
- (6)Java数据结构-- 转:JAVA常用数据结构及原理分析
JAVA常用数据结构及原理分析 http://www.2cto.com/kf/201506/412305.html 前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分源码,balab ...
- Java Web文件上传原理分析(不借助开源fileupload上传jar包)
Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web 最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...
- java并发包&线程池原理分析&锁的深度化
java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...
- Java 重入锁 ReentrantLock 原理分析
1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...
- Java 远程通讯技术及原理分析
在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI.MINA.ESB.Burlap.Hessian.SOAP.EJB和JMS等,这些 ...
- Java远程通讯技术及原理分析
在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI.MINA.ESB.Burlap.Hessian.SOAP.EJB和JMS等,这些 ...
- Java线程池应用及原理分析(JDK1.8)
目录 一.线程池优点 二.线程池创建 三.任务处理流程 四.任务缓存队列及排队策略 五.任务拒绝策略 六.线程池关闭 七.线程池实现原理 八.静态方法创建线程池 九.如何确定线程池大小 一.线程池优点 ...
- Java,JDK动态代理的原理分析
1. 代理基本概念: 以下是代理概念的百度解释:代理(百度百科) 总之一句话:三个元素,数据--->代理对象--->真实对象:复杂一点的可以理解为五个元素:输入数据--->代理对象- ...
随机推荐
- PCB MS SQL 行转列
一.原数据: SELECT inman,indate FROM [fp_db].[dbo].[ppezhpbb] WHERE indate > '2016-5-1' AND indate < ...
- Jenkins构建项目,JAVA_HOME is not defined correctly
好久都没有更新了,由于职位调整,开始捣鼓持续集成的东西了.jenkins的基本安装配置网上有很多教程,不用多讲了,就记录下我在使用过程中遇到的一些问题.话说这个jenkins环境以及安装好了有一段时间 ...
- NPOI导出Excel自动计算公式问题
以前用过sheet.ForceFormulaRecalculation = true;当时能够自动计算出来. 今天把模板改了一下(没动公式,但是模板是老板改的,我也不知道他操作了什么),结果就不能自动 ...
- # --with-http_sub_module模块
作用: http内容替换 语法 第一种语法: sub_filter string:要替换的内容 替换后的内容 这个模块只能替换第一个匹配的字符串,如果需要匹配全部替换,则用到下面的第三种语法配置 第二 ...
- supervisord 使用记录
#supervisor简介 Supervisor是一个 Python 开发的 client/server 系统,可以管理和监控类 UNIX 操作系统上面的进程. #组成部分 supervisord(s ...
- 总结:Ruby里是值传递还是引用传递
在ruby中一切都是对象,而你向方法中传递的实质上是对象的引用( object-reference).ruby中变量都是对象的引用. 先来看 def pref2(agr) agr.downcase e ...
- 【Oracle】设置快速恢复区及reset快速恢复区
快速恢复区 概念 是一个默认放置所有备份恢复操作有关文件的地方,包括:控制文件在线镜像.在线重做日志.归档日志.外来归档日志.控制文件镜像复制.数据文件镜像复制.RMAN备份片和闪回日志. 如果启用的 ...
- servlet-后台获取form表单传的参数
前台代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> & ...
- webstorm界面主题
http://www.webstormthemes.com/
- 对 Lua闭包 知识点的学习的总结 ,在这里和大家分享一下,希望对大家有所帮助
Lua闭包 --当一个函数内部嵌套另外一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征为词法定界.--闭包是由函数与其相关的引用环境组合而成的实体 闭包=函数+引用环境--不是在局部 ...