精华内容
下载资源
问答
  • 由于项目需求,最近花了较多时间来看开源项目代码,在本文中,简单总结一下对为什么要看源码、如何看源码这两个问题思考。看源码的意义看源码的意义总结起来包含但不限于以下几点:一、解决问题(BUG)二、知...

    908c3f1f18d56e0ba0029ea41926503d.png

    由于项目的需求,最近花了较多的时间来看开源项目的代码,在本文中,简单总结一下对为什么要看源码、如何看源码这两个问题的思考。

    看源码的意义

    看源码的意义总结起来包含但不限于以下几点:

    一、解决问题(BUG)

    二、知其所以然

    我在[如何学习新技术、团队技术选型时要注意些什么][Link 1]里面提到过,如果我们需要将一个开源项目用到自己的项目中,那么就必须了解这项项目的优缺点,并深知原理,对部分细节(尤其是项目的优势、feature)进行深入研究。

    先看文档,整体把握

    一般来说,文档是对代码的高度凝练,一个高质量的开源一般会包含tutorial、specification、API reference等documents,通过选择性的略读、精读这些文档,就能大致了解项目的整体架构、设计原则。6 大设计原则,你知道吗?

    正确的路线是通过文档去认识这个项目,然乎通过阅读代码去验证文档、深入细节,而不是通过直接啃源码来了解这个项目,以偏概全。

    理解代码组织,文件名,类名

    当需要看代码的时候,不要找到一个文件就开始,先看看代码组织,粗略看看文件名、类名,基本就能猜测到每一部分。比如redis的源码就组织得很好,基本上看文件名就可以快速定位每一个command的实现位置。

    关注一个问题,从问题追踪代码

    看源码的目标决定了此时此刻的关注点,不管是解决遇到的bug还是学习某个算法,都让我们聚焦到一个具体的问题,从这个具体的问题去追踪代码,忽略掉当前无需关注的细枝末节,步步深入,直达目标。

    当然在解决一个问题的时候,有可能会引发新的问题,尤其是学习的时候,此时只需记录新问题(放到收集篮,不要立即发散),待之前追踪的问题解决之后,再来看新发现的问题。

    解决一个issue

    如果自己没有问题,那么就帮忙解决别人的问题,通常来说,开源项目都有许多待解决的issue,从中选择一个入手即可。

    调试

    只要可以,一定先让代码编译通过、跑起来,这样不管是加log、打印调用栈还是断点调试都方便很多。尤其是对于像python这种动态类型代码,不跑起来很难知道到底在干啥。

    加注释,做笔记

    展开全文
  • css选择器万年不变优先级和权重我们在使用CSS对网页元素定义样式时经常会遇到这种情况:对一般元素应用一般样式,然后在更特殊元素上覆盖它们.那么我们怎么样来保证我们所新定义元素样式能覆盖目标元素上原有...

    css选择器万年不变的优先级和权重

    我们在使用CSS对网页元素定义样式时经常会遇到这种情况:要对一般元素应用一般样式,然后在更特殊的元素上覆盖它们.那么我们怎么样来保证我们所新定义的元素样式能覆盖目标元素上原有的样式呢? 在CSS中,会 ...

    shell命令:echo命令详解

    功能说明:显示文字. 语 法:echo [-ne][字符串] / echo [--help][--version] 补充说明:echo会将输入的字符串送往标准输出.输出的字符串间以空白字符隔开, 并在 ...

    (转)MATLAB入门教程

    MATLAB入门教程   1.MATLAB的基本知识 1-1.基本运算与函数    在MATLAB下进行基本数学运算,只需将运算式直接打入提示号(>>)之後,并按入Enter键即可.例如: ...

    详解 swift2.2 和 OC 的混编

    前言: 我们在一些情况下,仅仅使用swift 是无法完成一个项目的,在swift项目中必要用到 OC 实现一些功能,比如,项目要使用一些第三方的框架,但这个第三方的框架却是用 OC 实现的,或者你的项 ...

    Ajax异步获取html数据中包含js方法无效的解决方法

    页面上使用js写了一个获取后台数据的方法 function data() { var tab = $("#dic") $.ajax({ url: '../demo.ashx?met ...

    Centos7新功能

    Centos7 单用户模式   centos7里不再有0-6启动级别,而是4个target   graphical.target  多人模式,支持图形和命令行两种登录,对应之前的3,5级别   mul ...

    网易云安全两篇论文入选计算机视觉顶级会议ICCV

    本文由  网易云发布. 10月22日至29日,全球计算机视觉顶尖专家们共聚威尼斯,参加ICCV2017国际计算机视觉大会,就领域内最新成果展开集中研讨,大会论文集也代表了计算机视觉领域最新的发展方向和 ...

    css浮动与清除浮动

    css浮动 首先,我们要知道,css中块级元素在页面中是独占一行的,自上而下排列,也就是我们所说的流,通常称为标准流. 以div为例,div是块级元素,如下: 可以清楚地看到,div是独占一行的,di ...

    【转】[总结]vue开发常见知识点及问题资料整理(持续更新)

    1.(webpack)vue-cli构建的项目如何设置每个页面的title 2.vue项目中使用axios上传图片等文件 3.qs.stringify() 和JSON.stringify()的区别以及 ...

    使用 Sixel 图形格式在终端中显示缩略图

    不久前,我们讨论了 Fim,这是一个轻量级的命令行图像查看器应用程序,用于从命令行显示各种类型的图像,如 bmp.gif.jpeg 和 png 等.今天,我偶然发现了一个名为 lsix的类似工具.它类 ...

    展开全文
  • 看看他们的源码看看他们写的代码和我的代码有哪些的不同,同样的一个功能,哪些优秀的程序员们,是如何实现的,但这时,迷茫了,我应该去啥,哪个项目对我现在帮助最大,为了少走点弯路,来到这里,寻求一些...
  • 前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java。大多是线程代码,没办法,那...在深入每一个部分去一下线程及其相关包的源码做深入了解。目标:线程,并发包(线程池,并发的数据结构,...

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java。大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填一次。

    思路:大概看了线程相关的一些知识,对线程的运行机制,同步机制,以及整个系统都做一个全面的了解。在深入每一个部分去看一下线程及其相关包的源码做深入了解。

    目标:线程,并发包(线程池,并发的数据结构,锁,原子类)。

    通过一些资料的查看最终把目标定位在线程和并发包上,线程是核心,并发包是辅助工具,用于多线程运行时的并发问题。其实这样看来多线程并没有很多的东西,支持并发的数据结构用于保证数据的安全性,各种锁机制用来保证类,对象,方法,属性的并发安全。它的难点主要是在运用上,各种锁机制的运用,会给系统带来负担,也会给程序性能带来影响,但是同时又要保证数据的同步。锁机制使用的强度和位置,直接决定了并发系统的好坏。

    主要涉及到下面几个包:

    线程相关:java.lang下面的几个类

    接口摘要

    Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。

    类摘要

    线程 是程序中的执行线程。

    线程组表示一个线程的集合。

    该类提供了线程局部 (thread-local) 变量。

    并发包相关:

    1.Java.util.concurrent包

    枚举摘要

    TimeUnit 表示给定单元粒度的时间段,它提供在这些单元中进行跨单元转换和执行计时及延迟操作的实用工具方法。

    2.java.util.concurrent.locks包

    接口摘要

    Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。

    Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

    ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。

    3.java.util.conturrent.atomic

    类摘要

    可以用原子方式更新的 boolean 值。

    可以用原子方式更新的 int 值。

    可以用原子方式更新其元素的 int 数组。

    基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。

    可以用原子方式更新的 long 值。

    可以用原子方式更新其元素的 long 数组。

    基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。

    AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。

    基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。

    AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。

    对每个包里的部分类进行结构解析:

    1.Java.util.concurrent:这个包里,主要常用到的是数据结构Queue,MAP,List,和线程池

    *&*Queue队列相关的类图:

    126efba2d4f0f7100ce845035ab3d507.png

    *&*List和Set相关的类图:List和Set在并发包中的实现类有copyOwriteArrayList,CopyOnWriteArraySet,ConcurrentSkipListSet,三个类

    1003102fa9a8fcfa96707b4739ae7840.png

    *&*Map相关的类图:并发包中与Map相关的包括ConcurrentHashMap,ConcurrentSkipListMap两个类

    368d911ea36f9205de75794a2ae69a00.png

    这些都是和数据结构相关的,其中蓝色的部分表示的是并发包的内容,灰色部分表示其他包(大部分是util)中的内容。其中省略了一部分内容类和接口,因为很多不常用,而且用的时候一部分也不是作为一个数据结构来用的。这也不是这部分关注的重点。我们主要关注并发包相关的内容。这三张类图,应该可以让大家对并发包的数据结构有一个大致的了解,并发包还有一个内容就是线程池。

    *&*线程池类图:线程池重最终有两个实现类ThreadPoolExeutor和SheduleThreadPoolExeutor.但是我们一般不直接去实现这两个类去创建一个线程池,我们通常用Exeutors这个类来创建一个线程池,这个类中把线程池的创建和管理进行了封装,我们只需要运用这个类就可以创建一个线程池并进行管理。另外一个接口ThreadFactory主要是用来创建线程的,实现这个接口我们就拥有了一个线程工厂,创建线程更方便。

    15a4b93a1677460fb6f96856b8b42958.png

    2.Java.util.concurrent.Locks:(以下说明摘自API)为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。该框架允许更灵活地使用锁和条件,但以更难用的语法为代价

    Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是

    ReadWriteLock 接口以类似方式定义了一些读取者可以共享而写入者独占的锁。此包只提供了一个实现,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。

    Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。

    AbstractQueuedSynchronizer 类是一个非常有用的超类,可用来定义锁以及依赖于排队阻塞线程的其他同步器。

    前面两者都扩展了类 AbstractOwnableSynchronizer(一个帮助记录当前保持独占同步的线程的简单类)。LockSupport 类提供了更低级别的阻塞和解除阻塞支持,这对那些实现自己的定制锁类的开发人员很有用。

    f2a7e6b22c9e5ccc7aaa5e8bc188b712.png

    3.Java.util.concurrent.atomic:(以下内容摘自API)原子类这部分没有很复杂的类关系,主要是对基础的int,long,bolean变量,以及相关的数组和对象引用,提供了原子访问和更新的类。

    get 具有读取 volatile变量的内存效果。

    set 具有写入(分配)volatile变量的内存效果。

    除了允许使用后续(但不是以前的)内存操作,其自身不施加带有普通的非 volatile 写入的重新排序约束,lazySet 具有写入(分配)volatile 变量的内存效果。在其他使用上下文中,当为 null 时(为了垃圾回收),lazySet 可以应用不会再次访问的引用。

    weakCompareAndSet 以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。

    compareAndSet 和所有其他的读取和更新操作(如 getAndIncrement)都有读取和写入 volatile 变量的内存效果。

    除了包含表示单个值的类之外,此包还包含 Updater 类,该类可用于获取任意选定类的任意选定 volatile 字段上的 compareAndSet 操作

    AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 是基于反射的实用工具,可以提供对关联字段类型的访问。它们主要用于原子数据结构中,该结构中同一节点(例如,树节点的链接)的几个 volatile 字段都独立受原子更新控制。这些类在如何以及何时使用原子更新方面具有更大的灵活性,但相应的弊端是基于映射的设置较为拙笨、使用不太方便,而且在保证方面也较差。

    AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来说是不受支持的。

    AtomicMarkableReference 类将单个布尔值与引用关联起来。例如,可以在数据结构内部使用此位,这意味着引用的对象在逻辑上已被删除。

    AtomicStampedReference 类将整数值与引用关联起来。例如,这可用于表示与更新系列对应的版本号。

    花了两天时间看着一段的API,稍微整理了一下,大部分内容还是来自API,但是我个人觉得这种东西听起来很难入门,其实一大部分原因是我们没用从总体上去把握它,从最开始的接口和类的清单表中可能很多东西没有接触过,其实那都不是问题,当看到类图的时候清楚地看到他们之间的父子关系,其实最终需要我们去掌握的类不是很多。

    我个人认为,数据结构(queue,Map,List)这部分是最容易掌握的,因为它的内部机制已经实现了,我们只需要知道在什么场景需要使用它,什么时候的数据需要用这种并发安全的数据结构在经过不断地使用就能掌握(深入理解当我没说,哈哈)。线程池这部分也很容易掌握,我们只要熟悉Exeutors类,掌握里面的方法,就足够熟练的运用对线程池。然后是原子类,原子类访问的变量是volited修饰的,原子类实际上就是对数据进行了操作的原子性(操作是一个不可分割的整体,ex:updape包括读取数据,修改数据,写回数据三个步骤,普通的操作就不能保证update操作的原子性)一致性的封装,保证了对这些数据操作的时候是线程安全的。最难的应该是在锁上,加锁会造成线程阻塞,高并发状态下是否该用锁,和在什么地方用锁,锁的粒度很关键,本来只需要在数据上加锁,而我们却加在了方法上,或者对象上,那对性能的影响可能不是一星半点。各种锁的用法和技巧,带来的差异,弊端都需要清楚的知道。

    希望能给像我一样还未入门的朋友带来一点帮助,现在已经基本了解了框架,后面就是对这些部分的深入探索,从具体的应用中看差异,深入源码和底层的VM实现看原理,我相信一步步去做一定会逐渐上手。

    展开全文
  • 其实我看到已有很多大佬写过此类文章,并且写也比较...在你去分析源码的时候,首先会用,明白这个工具类做用,若是连一个工具类都包含哪些功能,这些功能做用都不清楚,我以为看源码就是一种煎熬。(固然,...

    其实我看到已有很多大佬写过此类文章,并且写的也比较清晰明了,那我为何要再写一遍呢?其实也是为了加深本身的印象,巩固本身的基础html

    (主要是不少文章没有写出来我想知道的东西!!!14600000390616471b005a5744dc46e78417291e.html​!!!!)java

    前言

    我说一下我认为怎么样才能去看懂,看透彻一个源码。

    在你去分析源码的时候,首先要会用,要明白这个工具类的做用,若是连一个工具类都包含哪些功能,这些功能的做用都不清楚,我以为看源码就是一种煎熬。(固然,大佬除外)api

    14600000390616501b005a5744dc46e78417291e.html​自我洗脑中~我是大佬!我是大佬!我是大佬!(he~tui!我不配!!!)数组

    正文

    本次是基于JDK1.8来具体分析ArrayList源码dom

    ArrayList的概念:

    动态数组,它提供了动态的增长和减小元素,实现了Collection和List接口,灵活的设置数组的大小等好处。

    每一个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它老是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增加。函数

    一、继承结构分析

    咱们先来看一下ArrayList类的继承结构:工具

    14600000390616461b005a5744dc46e78417291e.html

    Java支持单继承,多实现源码分析

    AbstractList:性能

    抽象接口类,目的是使用抽象类中已经实现的方法。

    咱们点开AbstractList源码,会看到其实AbstractList已经也实现了List接口,为何要先继承AbstractList,而让AbstractList先实现List?而不是让ArrayList直接实现List?优化

    这里是有一个思想,接口中全都是抽象的方法,而抽象类中能够有抽象方法,还能够有具体的实现方法,正是利用了这一点,让AbstractList实现接口中一些通用的方法,而如ArrayList就继承这个AbstractList类,拿到一些通用的方法,而后本身在实现一些本身特有的方法,这样一来,让代码更简洁,就继承结构最底层的类中通用的方法都抽取出来,先一块儿实现了,减小重复代码。因此通常看到一个类上面还有一个抽象类,应该就是这个做用。

    List:

    使用List的接口规范

    RandomAccess:

    这个是一个标记性接口,经过查看api文档,它的做用就是用来快速随机存取,有关效率的问题,在实现了该接口的话,那么使用普通的for循环来遍历,性能更高,例如arrayList。而没有实现该接口的话,使用Iterator来迭代,这样性能更高,例如linkedList。因此这个标记性只是为了让咱们知道咱们用什么样的方式去获取数据性能更好。

    Cloneable:

    Serializable:

    实现该序列化接口,代表该类能够被序列化,什么是序列化?简单的说,就是可以从类变成字节流传输,而后还能从字节流变成原来的类。

    🤔🤔🤔🤔为何AbstractList已经实现了List,ArrayList还要再实现一次呢?

    其实ArrayList再去实现一次List在这里并无什么实际意义,这实际上是一个错误,由于做者写这代码的时候以为这个会有用处,可是其实并没什么用,但由于没什么影响,就一直留到了如今。有兴趣的同窗能够去研究一下。

    二、类分析

    public class ArrayList extends AbstractList

    implements List, RandomAccess, Cloneable, java.io.Serializable

    {

    private static final long serialVersionUID = 8683452581122892189L;

    /**

    * 缺省容量

    */

    private static final int DEFAULT_CAPACITY = 10;

    /**

    * 有参构造缺省空数组

    */

    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**

    * 无参构造缺省空数组

    */

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**

    * 数组元素(实际操做的数组,新增,删除等方法都是在此数组发生操做)

    */

    transient Object[] elementData; // non-private to simplify nested class access

    /**

    * 实际数组的大小

    */

    private int size;

    /**

    * 数组的最大容量

    */

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    1b005a5744dc46e78417291e.html

    这里分析几个地方:

    (1)为何数组最大容量是Integer.MAX_VALUE - 8,而不是Integer.MAX_VALUE?

    其实源码中给了备注:意思应该是有些虚拟机在数组中保留了一些头信息。避免内存溢出!

    /**

    * The maximum size of array to allocate.

    * Some VMs reserve some header words in an array.

    * Attempts to allocate larger arrays may result in

    * OutOfMemoryError: Requested array size exceeds VM limit

    */

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    1b005a5744dc46e78417291e.html

    (2)为何定义了两个空数组?

    首先定义空数组的根本缘由是

    优化处理,若是一个应用中有不少这样ArrayList空实例的话,就会有不少的空数组,无疑是为了优化性能,全部ArrayList空实例都指向同一个空数组。二者都是用来减小空数组的建立,全部空ArrayList都共享空数组。二者的区别主要是用来起区分做用,针对有参无参的构造在扩容时作区分走不一样的扩容逻辑,优化性能。

    (3)elementData为何定义成transient?

    三、构造方法

    14600000390616451b005a5744dc46e78417291e.html

    Array List总共有三个构造方法,下面咱们一一分析

    1)无参构造方法 ArrayList()

    /**

    * 将空数组初始化大小为10(将空数组初始化大小为10,具体在何时初始化大小为10,待会儿会说到)

    */

    public ArrayList() {

    // 将elementData元素数组初始化为空数组

    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

    }

    1b005a5744dc46e78417291e.html

    无参构造方法中,将元素数组elementData初始化为空数组。(注意:这里就体现了我上文说到的,为何定义两个空数组)

    2)有参构造方法 ArrayList(int)

    /**

    * 构造一个具备指定初始容量的列表

    *

    * @param initialCapacity: 初始化数组的值

    */

    public ArrayList(int initialCapacity) {

    //若是初始化的值大于0,则给定elementData一个长度为initialCapacity的数组

    if (initialCapacity > 0) {

    this.elementData = new Object[initialCapacity];

    } else if (initialCapacity == 0) { // 若是初始化的值等于0,则初始化为空数组

    this.elementData = EMPTY_ELEMENTDATA;

    } else { //不然(小于0的状况)抛出异常

    throw new IllegalArgumentException("Illegal Capacity: "+

    initialCapacity);

    }

    }

    1b005a5744dc46e78417291e.html

    3)有参构造方法 ArrayList(Collection extends E> c)

    /**

    * 构造一个指定元素的集合(此方法不太经常使用)

    * @param c

    */

    public ArrayList(Collection extends E> c) {

    // 将集合转换为数组并赋值给elementData

    elementData = c.toArray();

    // 若是集合的大小不为0

    if ((size = elementData.length) != 0) {

    // 若是转换后的数组不是泛型(object),则须要用Arrays的工具转换一下为object数组(这里再也不对Arrays.copyOf展开论述)

    if (elementData.getClass() != Object[].class)

    elementData = Arrays.copyOf(elementData, size, Object[].class);

    } else { // 不然初始化elementData为一个空数组

    this.elementData = EMPTY_ELEMENTDATA;

    }

    }

    1b005a5744dc46e78417291e.html

    对于当前构造方法,我举个例子,更清晰明了

    14600000390616491b005a5744dc46e78417291e.html

    四、经常使用方法源码分析

    boolean add(E e)

    重中之重,ArrayList的核心奥秘!!!!

    /**

    * 在数组中增长一个元素

    * @param e 元素对象

    */

    public boolean add(E e) {

    // 肯定内部容量是否够用,size是元素数组中数据的个数,由于要添加一个元素,因此size+1,先判断size+1的这个个数数组可否放得下,就在这个方法中去判断是否数组.length是否够用了。

    ensureCapacityInternal(size + 1);

    // 将元素e赋值到elementData末尾

    elementData[size++] = e;

    return true;

    }

    // 此方法能够理解为中转计算

    private void ensureCapacityInternal(int minCapacity) {

    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {

    // 判断数组是否是空数组, 若是是空数组(此时minCapacity = 0 + 1 = 1),就将minCapacity初始化为10,但此时仅仅是返回要初始化数组的大小,并无真正初始化数组为10

    // private static final int DEFAULT_CAPACITY = 10;

    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

    // Math.max(参数1,参数2)方法的意思是返回参数中最大的数,若是是空数组是,此时返回的是10

    return Math.max(DEFAULT_CAPACITY, minCapacity);

    }

    // 若是初始化的集合不是空,则返回元素数组的size + 1

    return minCapacity;

    }

    // 别担忧 我有奥妙全自动(奥妙洗衣粉~全国人民都知道~~)

    private void ensureExplicitCapacity(int minCapacity) {

    // 结构变化记录+1 在父类AbstractList中定义了一个int型的属性:modCount,记录了ArrayList结构性变化的次数

    modCount++;

    // 判断数组是否够用,若是不够用,则自动扩容

    // 一、当初始化的集合为空数组时,此时minCapacity是10,而elementData的长度为0,因此须要扩容

    // 二、当初始化的集合不为空是,也就是给定了大小,或已经初始化了元素,此时的minCapacity = 实际数组个数+1,此时判断集合不够用,也须要进行扩容,不然元素会溢出

    if (minCapacity - elementData.length > 0)

    grow(minCapacity);

    }

    // 自动扩容

    private void grow(int minCapacity) {

    // oldCapacity:元素数组的实际长度(即扩充前的数组大小)

    int oldCapacity = elementData.length;

    // oldCapacity 扩容1.5倍赋值给newCapacity( >>为右移运算符,至关于除以2 即oldCapacity/2 )

    int newCapacity = oldCapacity + (oldCapacity >> 1);

    // 若是初始化为空的状况,则将数组扩容为10,此时才是真正初始化元素数组elementData大小为10

    if (newCapacity - minCapacity < 0)

    newCapacity = minCapacity;

    // 若是1.5倍的数组大小超过了集合的最大长度,则调用hugeCapacity方法,从新计算,也就是给定最大的集合长度

    if (newCapacity - MAX_ARRAY_SIZE > 0)

    newCapacity = hugeCapacity(minCapacity);

    // 已经肯定了大小,就将元素copy到elementData元素数组中~~

    elementData = Arrays.copyOf(elementData, newCapacity);

    }

    private static int hugeCapacity(int minCapacity) {

    // 内存溢出判断

    if (minCapacity < 0)

    throw new OutOfMemoryError();

    // 这里的逻辑为:若是须要扩容的大小比数组的最大值都大,就返回Integer,MAX_VALUE(int最大值),不然返回集合的最大值(int最大值-8)

    return (minCapacity > MAX_ARRAY_SIZE) ?

    Integer.MAX_VALUE :

    MAX_ARRAY_SIZE;

    }

    1b005a5744dc46e78417291e.html

    void add(int index, E element)

    增长元素到指定下标

    /**

    * 增长元素到指定下标

    *

    * @param index 下标

    * @param element 元素

    */

    public void add(int index, E element) {

    // 参数校验

    rangeCheckForAdd(index);

    // 此方法再也不赘述,参考上文Add方法重的论述

    ensureCapacityInternal(size + 1);

    // 请看下面代码块的注释

    System.arraycopy(elementData, index, elementData, index + 1,

    size - index);

    // 将指定元素覆盖到指定下标

    elementData[index] = element;

    // 长度size + 1

    size++;

    }

    /**

    * 适用于add 和 addAll的校验方法

    */

    private void rangeCheckForAdd(int index) {

    if (index > size || index < 0)

    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }

    1b005a5744dc46e78417291e.html

    System.arraycopy 方法解析

    /**

    * System提供了一个静态方法arraycopy(),咱们可使用它来实现数组之间的复制

    * 函数为:public static native void arraycopy(Object src,int srcPos,Object dest, int destPos,int length);

    * @param src the source array. 源数组

    * @param srcPos starting position in the source array. 源数组的起始位置

    * @param dest the destination array. 目标数组

    * @param destPos starting position in the destination data. 目标数组的起始位置

    * @param length the number of array elements to be copied. 复制的长度

    //举个例子

    public static void main(String[] args){

    int[] arr = {1,2,3,4,5};

    int[] copied = new int[10];

    System.out.println(Arrays.toString(copied));

    System.arraycopy(arr, 0, copied, 1, 5);//5是复制的长度

    System.out.println(Arrays.toString(copied));

    }

    输出结果为:

    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    [0, 1, 2, 3, 4, 5, 0, 0, 0, 0]

    1b005a5744dc46e78417291e.html

    boolean remove(Object o)

    根据元素进行删除

    public E remove(int index) {

    // 参数校验

    rangeCheck(index);

    // 结构变化记录+1

    modCount++;

    // 获取旧数据,返回给开发人员,目的是让开发人员知道删除了哪一个数据

    E oldValue = elementData(index);

    // 计算须要元素须要移动的次数

    int numMoved = size - index - 1;

    if (numMoved > 0)

    // 同上文叙述

    System.arraycopy(elementData, index+1, elementData, index,

    numMoved);

    // 将最后一个元素置为空(元素前移,最后一位置为空),让GC回收

    elementData[--size] = null;

    return oldValue;

    }

    private void rangeCheck(int index) {

    if (index >= size)

    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }

    1b005a5744dc46e78417291e.html

    void clear()

    清空集合

    /**

    * 清空集合

    */

    public void clear() {

    modCount++;

    // 将数组置为空,促使GC回收

    for (int i = 0; i < size; i++)

    elementData[i] = null;

    size = 0;

    }

    1b005a5744dc46e78417291e.html

    E get(int index)

    返回此列表中指定位置上的元素

    /**

    * 检查给定的索引是否在范围内。 若是没有,则抛出一个适当的运行时异常。

    * @param index : 下标

    */

    public E get(int index) {

    // 校验下标有效性

    rangeCheck(index);

    // 返回元素数组中指定index位置的数据

    return elementData(index);

    }

    private void rangeCheck(int index) {

    // 若是下标大于实际数组长度(元素数组最后一个数据下标为size-1)

    if (index >= size)

    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }

    1b005a5744dc46e78417291e.html

    E set(int index, E element)

    /**

    * 覆盖相应下标的数据

    * @param index 下标

    * @param element 元素数据

    */

    public E set(int index, E element) {

    // 校验方法(再也不解释,与get方法中同样)

    rangeCheck(index);

    // 获取到旧数据,这里将旧数据返回出去,为了让开发者知道替换的是哪一个值

    E oldValue = elementData(index);

    // 将指定下标覆盖为新元素

    elementData[index] = element;

    return oldValue;

    }

    1b005a5744dc46e78417291e.html

    结尾

    其实ArrayList中还有不少不少方法,这里就不在一一叙述了,由于你理解了本文中所说的源码,其实其它再去理解,再去查看,是比较容易简单的。

    1460000039061648

    1b005a5744dc46e78417291e.html

    本篇文章就讲到这里,若是有写的很差的地方,请多多指教,我是善良的小黑哥,但愿与你们共同进步!!

    展开全文
  • 构造方法:看看做了哪些事,跟踪方法里面方法 常用方法:与构造方法类型,看看该方法是如何实现 二、ArrayList源码分析: 1、数据结构: ArrayList是基于数组,数组元素类型为Object,即可以存放所有...
  • 既然是源码那我们怎么一个类的源码呢?这里我推荐的方法是:1)继承结构这个类的层次结构,处于一个什么位置,可以在自己心里有个大概的了解。2)构造方法在构造方法中,看做了哪些事情,跟踪方法中里面的...
  • 既然是源码那我们怎么一个类的源码呢?这里我推荐的方法是: 1)继承结构 这个类的层次结构,处于一个什么位置,可以在自己心里有个大概的了解。 2)构造方法 在构造方法中,看做了哪些事情,...
  • 按照本人面试经验来说,面试主要几点:项目经验+基本技术+个人潜力说到这里,也给大家推荐一个架构交流学习群:835544715,里面会分享一些资深架构师录制视频录像:有Spring,MyBatis,Netty源码分析,高并发...
  •  既然是源码那我们怎么一个类的源码呢?这里我推荐的方法是:  1)继承结构  这个类的层次结构,处于一个什么位置,可以在自己心里有个大概的了解。  2)构造方法  在构造方法中,看做了哪些事...
  • JAVA集合容器源码解析

    2019-04-29 11:27:58
    集合容器是我们使用最多一个容器,像ArrayList,LinkList,HashMap等等,这个...另外,我觉得看源码要有目的性,你要怎么看,需要看哪些。对于我自己,我主要从以下方面来看源码: 1、类说明 2、字段 3、主...
  •  既然是源码那我们怎么一个类的源码呢?这里我推荐的方法是:  1)继承结构  这个类的层次结构,处于一个什么位置,可以在自己心里有个大概的了解。  2)构造方法  在构造方法中,看做了哪些事...
  • 新手学习 Java,有哪些 Java 相关博客,专栏,和技术学习网站推荐? Java 还是大数据,你需要了解这些东西! 其他 贡献者 你可以点此链接查看JavaGuide所有贡献者。 感谢你们让 JavaGuide 变得更好!如果...
  • 新手学习 Java,有哪些 Java 相关博客,专栏,和技术学习网站推荐? Java 还是大数据,你需要了解这些东西! 其他 贡献者 你可以点此链接查看JavaGuide所有贡献者。 感谢你们让 JavaGuide 变得更好!如果...
  • 学习之前,先来看看 Issues 讨论区技术面试官是怎么说吧。本项目欢迎各位开发者朋友到 Issues 讨论区分享自己一些想法和实践经验。也不妨 Star 关注 doocs/advanced-java,随时追踪项目最新动态。 本项目基于 ...
  • 队列在源码方面面试题,一般面试官会从锁,线程池等知识点作为问题入口,慢慢问到队列,由于锁、线程池咱们还没有学习到,所以本章就直奔主题,从队列入手,看看队列都有哪些面试题(队列种类很多,本文在说队列...
  • Java集合:ArrayList源码分析

    多人点赞 2021-01-19 17:21:21
    在你去分析源码的时候,首先会用,明白这个工具类作用,如果连一个工具类都包含哪些功能,这些功能作用都不清楚,我觉得看源码就是一种煎熬。(当然,大佬除外) 正文: 本次是基于JDK1.8来具体分析...
  • 实习工作对求职者的要求不高,但是至少掌握Java的基础知识,千锋重庆Java培训机构的小编这里根据行业最新变化和企业用人需求整理了一份Java开发系统全面的学习路线,大家可做一些了解与参考。 01、Java基础 认真...
  • 花费一月时间吐血整理Java程序员自我学习的书籍列表,收录书籍尽量都保证同类书籍的豆瓣评分最高,帮助想入坑Java的同学,根据这个书籍列表从小白按图索骥逐步晋级成大神 每本书都尽量提供电子版和纸质书籍地址;...
  • Java程序员一定学会这个内部类技巧,不然很难成为架构师过了很多源码,都有用到内部类,但是自己以前在生产环境上,用确实少,也有用过但是很少,所以今天就打算好好把它从头到尾过一遍。定义可以将一个...
  • HashMap 一、数据结构 ...↓看看源码吧 jdk1.7.0_80 插入key-value时主要有两个步骤: 1.根据keyhash值,找到key-value存放桶(bucketIndex),也就是对应数组元素下标。 2.当不同key发生哈希冲突时...
  • 首先我们看看和Stream相关内容都有哪些:Stream上操作前面我们介绍过Stream上操作分为两大类:中间操作和终端操作。其中中间操作作用是把Stream转换为另外一个Stream,终端操作是开启Stream计算过程。所以...
  • 实习工作对求职者的要求不高,但是至少掌握Java的基础知识,千锋重庆Java的小编这里根据行业最新变化和企业用人需求整理了一份Java开发系统全面的学习路线,大家可做一些了解与参考。 01、Java基础 认真一遍 ...
  • 其中更进一步可以看看 ArrayList 的源码(这部分源码几乎没有什么难点),有助于理解接口和抽象类的使用。 另外,针对目前主要的java面试,我觉得java内存模型、GC、线程安全、线程池这些需要了解,不管面试会...
  • 知道已发布哪些更改,请查看以获取完整更改列表。 想知道我们下一个功能或修订版本何时发布? 注意我们计划。 :pushpin: 为了使用框架,重要知道它优点。 让我们看看该框架主要功能是什么: ...
  • 这篇文章一开始我以为会比较简单,但是在深入源码分析时,遇到了很大的阻碍,比前面我们分析AQS以及读写锁的源码要难理解的多,断断续续也写了4天了。 如果你完还是没有理解的话,那我在这里表示深深的歉意,同时...

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 177
精华内容 70
关键字:

java的源码要看哪些

java 订阅