从源码的角度分析List与Set的区别
很多时候我们在讨论List与Set的异同点时都在说:
1、List、Set都实现了Collection接口
2、List是有序的,可以存储重复的元素,允许存入null
3、Set是无序的,不允许存储重复的元素,只允许存入一个null
4、List查询效率高,但插入删除效率低
5、Set检索元素效率低、但删除插入效率高
6、List可以通过索引操作元素,Set不能根据索引获取到元素
这里基于ArrayList/HashSet(jdk1.8)的角度进行分析两者的异同点:
一、从各自所继承的父类以及实现接口,可知两者的源头是一致的,都是从Collection接口延伸出来

二、创建ArrayList实例
不指定初始容量时,创建一个空的对象数组,与原有注释不一致,注释写着创建一个长度为10的数组,实际是在添加元素时进行判断处理的。还可以指定容量创建相应长度的数组、也可以传入一个集合来进行创建,此处就不再详细列出
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
由创建实例可知,ArrayList底层是由数组实现的,因是数组的方式实现,而数组实际是有序数据的集合,所以List也就相应是有序的,且也是能存入null。
三、创建HashSet实例
在不指定容量的情况下创建HashSet时,直接是去创建一个HashMap实例,用HashMap来实现HashSet的相关功能。可以指定容量和负载因子等进行创建,实际也是HashMap的其他构造方法
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
由创建实例可知,底层实现为HashMap,既然是HashMap,按HashMap的规则,我们也就可知HashSet是无序的,因为HashMap的存储方式是先用key进行hash找到相应位置,然后再在该位置存储对应的key和value。HashSet不允许存储重复元素,是因为HashSet在添加元素时,是用该元素作为key,一个空的对象作为value进行存储的,也就相应的只能存储一个null
具体可见下图:
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
四、List查询效率高,但插入删除效率低
a、ArrayList是数组的方式实现,数组在内存中是连续且成块存储的,在查询时直接根据索引来获取数组相应值,所以查询效率会比较高
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
rangeCheck(index); return elementData(index);
}
b、在做插入和删除操作时,如果直接添加一个元素,不指定索引时,直接添加到数组的末尾,也会比较快,如果需要按索引去添加时就需要对原有数组的相应后续元素进行复制移位再进行对该索引对应的位置进行赋值,删除同样,把该索引的后续元素复制前移一位,然后把最后一个索引置空待GC,因此插入和删除操作效率会相对较低
//直接添加元素,在数组的最后添加(在添加元素之前,先判断数组长度是否满足,如果第一次添加,则创建一个长度为10的数组,如果非第一次添加,则判断数组长度是否充足,如果不长度不够则创建一个长度为(oldCapacity + (oldCapacity >> 1))的新数组,
//并把旧舒服复制到新的数组中来)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} //先判断索引是否有效,再判断数组长度是否充足,不充足则参照上面流程添加新数组,再进行数组的复制移位
public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
} //如上面解释
public E remove(int index) {
rangeCheck(index); modCount++;
E oldValue = elementData(index); int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}
五、Set检索元素效率低、但删除插入效率高
HashSet没有get单个值得方法。在检索元素时,需要先获取map的所有数据,再进行遍历比对,所以效率会比ArrayList低。而在删除插入时,根据map的特性,只需要要对所插入的对象作为key进行hash找到相应位置,然后放入该元素即可,所以相对ArrayList的需要进行数组的复制移位来说效率会相对较高
//如上面解释,HashSet的remove实际也就是map的remove
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
六、List可以通过索引操作元素,Set不能根据索引获取到元素
由上面分析可知,ArrayList是数组实现,存在索引可操作元素,而HashSet是HashMap实现,需要对元素进行hash找到位置再存储,不存在直接索引获取的方式
从源码的角度分析List与Set的区别的更多相关文章
- 从源码的角度分析ViewGruop的事件分发
从源码的角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View ...
- 第九节:从源码的角度分析MVC中的一些特性及其用法
一. 前世今生 乍眼一看,该标题写的有点煽情,最近也是在不断反思,怎么能把博客写好,让人能读下去,通俗易懂,深入浅出. 接下来几个章节都是围绕框架本身提供特性展开,有MVC程序集提供的,也有其它程序集 ...
- 从Android源码的角度分析Binder机制
欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 前言 大家好,好久不见,距离上篇文章已经有35天之久了,因为身体不舒服 ...
- [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...
- 从源码的角度解析View的事件分发
有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...
- 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...
- 从源码的角度解析Mybatis的会话机制
坐在我旁边的钟同学听说我精通Mybatis源码(我就想不通,是谁透漏了风声),就顺带问了我一个问题:在同一个方法中,Mybatis多次请求数据库,是否要创建多个SqlSession会话? 可能最近撸多 ...
- Android AsyncTask完全解析,带你从源码的角度彻底理解
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...
- 从源码的角度看Activity是如何启动的
欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 大家好,今天想与大家一起分享的是Activity.我们平时接触的最多的 ...
随机推荐
- php扩展编译方法
linux下php已经编译,如何再为php增加新的扩展通过php自带的phpize,如我的phpize在/usr/local/php/bin/phpize1.到软件的官方或pecl.php.net去下 ...
- JavaScript模拟自由落体
1.效果图 2.实现分析 利用Canvas画圆球.地面: 1.下落过程 物理知识回顾,物体下落过程(不计损耗)由重力势能转换成动能 重力势能 Ep = mgh 动能 Ek = (1/2)mv^2 速 ...
- Django之FBV与CBV
一.FBV与CBV FBV(function based views),即基于函数的视图:CBV(class based views),即基于类的视图,也是基于对象的视图.当看到这个解释时,我是很萌的 ...
- 【原】PHP从入门到精通2小时【图文并茂】
原创内容,转载请注明. 主要内容: 搭建PHP开发环境 第一个helloworld程序 变量 全局变量 循环结构 函数 数组 面向对象编程 继承 接口 多态 日志 文件的读写 时间格式和时区 创建图形 ...
- sql 去掉 空格
一.问题描述: 数据库中字段 nvarchar类型 存放数据如下: '3.3×10' 二.解决方案: --测试用例CREATE TABLE #temp(NAME NVARCHAR(20) null) ...
- C# ACtiveMQ 收发数据
1.下载ActiveMQ 官方网站下载地址:http://activemq.apache.org/ 2.运行ActiveMQ 解压缩apache-activemq-5.10.0-bin.zip,然后双 ...
- 【Tomcat】详解tomcat的连接数与线程池
前言 在使用tomcat时,经常会遇到连接数.线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector). Connector的主要功能,是接收连接请求,创建Req ...
- C++ enum的使用
enum day {Sun,Mon,Tue,Wed,Thu,Fri,Sat}; 默认情况下,枚举符的值从0开始,其后值总是前面一个+1. 即Sun=0,Mon=1,Tue=2,Wed=3,Thu= ...
- Mybatis逆向工程生成po、mapper接口、mapper.xml
Mybatis逆向工程生成po.mapper接口.mapper.xml 一.新建一个maven工程 请查看我的另一篇博客:<使用idea创建一个maven工程> 二.引入所需依赖 需要my ...
- HTML的语义化和一些简单优化
1.什么是语义化? 必应网典的解释 语义化是指用合理HTML标记以及其特有的属性去格式化文档内容.通俗地讲,语义化就是对数据和信息进行处理,使得机器可以理解. 语义化的(X)HTML文档有助于提升你的 ...