很多时候我们在讨论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&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;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的区别的更多相关文章

  1. 从源码的角度分析ViewGruop的事件分发

    从源码的角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View ...

  2. 第九节:从源码的角度分析MVC中的一些特性及其用法

    一. 前世今生 乍眼一看,该标题写的有点煽情,最近也是在不断反思,怎么能把博客写好,让人能读下去,通俗易懂,深入浅出. 接下来几个章节都是围绕框架本身提供特性展开,有MVC程序集提供的,也有其它程序集 ...

  3. 从Android源码的角度分析Binder机制

    欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 前言 大家好,好久不见,距离上篇文章已经有35天之久了,因为身体不舒服 ...

  4. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  5. 从源码的角度解析View的事件分发

    有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...

  6. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

  7. 从源码的角度解析Mybatis的会话机制

    坐在我旁边的钟同学听说我精通Mybatis源码(我就想不通,是谁透漏了风声),就顺带问了我一个问题:在同一个方法中,Mybatis多次请求数据库,是否要创建多个SqlSession会话? 可能最近撸多 ...

  8. Android AsyncTask完全解析,带你从源码的角度彻底理解

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...

  9. 从源码的角度看Activity是如何启动的

    欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 大家好,今天想与大家一起分享的是Activity.我们平时接触的最多的 ...

随机推荐

  1. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  2. Java进阶之JDBC

    一.相关概念 1.什么是JDBC JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它 ...

  3. 基于Asp.Net Core 2.1的简单问答社区系统源代码分享

    看见园子里很多人都在分享源代码,我也来凑个热闹. 该项目基于.NET CORE 2.1(其实是从1.1开始开发的),经历过不停的调整终于有个能拿出手的版本了,第一次在博客园发文章. 使用到的技术以及框 ...

  4. mysql中的游标使用

    1.游标的作用及属性 游标的作用就是用于对查询数据库所返回的记录进行遍历,以便进行相应的操作:游标有下面这些属性: a.游标是只读的,也就是不能更新它: b.游标是不能滚动的,也就是只能在一个方向上进 ...

  5. Android目录结构及作用

    1.add-ons-->Google API .比如GoogleMaps 2.build-tools-->各版本SDK编译工具. 3.docs-->离线开发者文档Android SD ...

  6. Dependency Walker使用说明 转载

    转载地址:http://blog.csdn.net/swort_177/article/details/5426848?reload 在Windows世界中,有无数块活动的大陆,它们都有一个共同的名字 ...

  7. 在CentOS下的docker容器中部署spring boot应用的两种方式

    我们通常在 windows 环境下开发 Java,而通常是部署在Linux的服务器中,而CentOS通常是大多数企业的首选,基于Docker的虚拟化容器技术,多数Java应用选择这种方式部署服务.本文 ...

  8. 前端学习 之 Bootstrap (一)

    中文文档 一.前言 1.简介 Bootsrtap作为一个前端框架,开箱即用,封装了前段开发的大量底层细节,开放灵活,对响应式设计网页很好支持,组件丰富,社区活跃. 特点: 可重用性 一致性 灵活的栅栏 ...

  9. 前端面试整理——javascript算法和测试题

    (1)算法: 1.斐波那契数列:1.1.2.3.5.8.13.21.输入n,输出数列中第n位数的值. 方案一: function fn(n){ var num1 = 1, num2= 1, num3 ...

  10. 微信小程序地图报错——ret is not defined

    刚刚在使用微信的map做地图时候 发现如下报错: 后来找了一会发现错误为经纬度写反了导致经纬度超出了范围 正确取值范围: latitude   纬度  浮点数,范围 -90 ~ 90 longitud ...