本篇内容包括:ArrayList 概述、ArrayList 的扩容机制(包含源码部分)、如何在遍历 ArrayList 时正确的移除一个元素、ArrayList 的构造方法及常用方法、关于 Array...那如果我们不断的往里面添加数据的话,ArrayList 是如何进行扩容的呢 ?...{}"即第一次调用add(E e),重新定义minCapacity的值,赋值为DEFAULT_CAPACITY=10 // 即第一次调用add(E e)方法时,定义底层数组elementData...方式二:使用迭代器遍历 ArrayList 并删除元素(推荐)。...contains(Object o) 如果此列表包含指定的元素,则此方法返回true boolean isEmpty() 如果此列表为空,则此方法返回true void ensureCapacity(
最近在写代码的时候,发现在定义一个空的列表时,使用list.add方法向列表中添加一个元素,会抛出空指针的异常。...但是由此就想到为什么new一个ArrayList的时候,调用ArrayList.add方法向ArrayList中添加一个元素的时候不会报空指针的异常呢?ArrayList是如何处理的呢?...因此,借此机会也阅读了一下ArrayList.add的相关源码。 在ArrayList的构造方法中中,ArrayList无参构造方法默认是一个空数组,但注释说是容量为10的数组。...接下来看ensureCapacityInternal方法: 第1行判断elementData是否是一个空数组(初始化容量为0,或者调用无参构造函数),如果是,则执行第#2行 第2行选取minCapacity...这种情况发生在ArrayList容量为空的情况,即oldCapacity=0,minCapacity=1时。
概述 ArrayList 是 List 接口的一个实现类,也是 Java 中最常用的容器实现类之一,可以把它理解为「可变数组」。 我们知道,Java 中的数组初始化时需要指定长度,而且指定后不能改变。...扩容实现原理 上面分析了 ArrayList 的构造器,但 ArrayList 如何做到动态扩容呢?...如何扩容的呢?...场景分析一: 若有一个初始容量为 1 的 ArrayList,线程 T1 和 T2 同时向其中添加元素(add() 方法),当添加第 2 个元素时,需要进行扩容。 此时若有以下执行时序: 1....场景分析二: 有一个 ArrayList,线程 T1 对其进行遍历;线程 T2 对其遍历,并移除部分元素。
,并实例化一个User放到ArrayList中,然后调用setter方法给user对象的friends属性赋值。...在init方法中对friends中的friend对象进行赋值 List friends = new ArrayList(); User friend = new User(); friend.setUserId...set = validator.validate(User,User.LoginGroup.class); } 执行测试 登录场景下只对用户ID进行了校验忽略了用户邮箱,输出了用户ID为空的提示信息...){ set = validator.validate(User,User.RegisterGroup.class); } 执行测试 注册场景下只对用户的邮箱进行校验忽略了用户ID的校验,并抛出了用户邮箱为空的提示信息...testGroupSequenceValidation(){ set = validator.validate(User,User.Group.class); } 执行该测试方法 控制台输出了userId为空的提示信息
如何避免空指针异常 使用对象之前一定要进行初始化,或者对是否初始化进行校验 不要设置函数返回值为null 针对接收的对象一定要进行判断 三、自动拆箱空指针异常 现象6:赋值时自动拆箱出现空指针异常...自动拆箱引发的空指针: 变量赋值自动拆箱引发空指针 方法传递参数自动拆箱引发空指针 现象7:变量赋值时自动拆箱的空指针异常 在test包下新建UnboxingNullPointExceptionTest...拆箱是通过调用包装器类的 xxxValue 方法实现的,也就是说当包装类为空时调用方法就会出现空指针异常 基本数据类型和引用数据类型,优先考虑基本数据类型 对于不确定的包装器类型进行判断校验 对于值为...null的包装类赋值为0 四、String、ArrayList 的空指针情况 现象10:字符串使用equals()方法比较时空指针 新建测试类StringNullPointExceptionTest,新增测试方法...,通过.属性的方式赋值会报错空指针 现象12:ArrayList执行addAll(null)时空指针 在ListNullPointExceptionTest测试类中新增方法 @Test public void
随着向 ArrayList 中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造 ArrayList 时指定其容量。...如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。...先将参数转化为数组: 如果长度为0,将elementData赋值为EMPTY_ELEMENTDATA 长度不为0 并且不是 Object[].class,则将elementData赋值为参数转化的数组...如果扩容之后的长度比minCapacity小,则赋值长度为minCapacity。...从index位置开始赋值到原数组。
3)看常用的方法 跟构造方法一样,这个方法实现功能是如何实现的 注:既然是源码,为什么要这样设计类,有这样的继承关系。这就要说到设计模式的问题了。...该类封装了一个动态再分配的Object【】数组,每一个类对象都有一个capacity属性,表示他们所封装的Object【】数组长度,当向ArrayList中添加元素时,该属性值会自动增加。...ArrayList的用法和Vector向类似,但是Vector是一个较老的集合,具有很多缺点,不建议使用。...说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的。...空的object[]会默认赋值为10,后面会提到什么时候赋值 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 备注: transient
接下来我们从List到具体实现类,详细分析一下其功能和实现: ?...首先返回从列表最后一个元素开始的列表迭代器,如果入参为null,从尾部向前遍历,找到为null的元素并返回位置;如果入参非null,也是从尾部向首部遍历找到相等的元素并返回位置;如果没有找到返回-1...如果入参小于0报参数非法异常;否则新建一个长度为入参的Object数组并赋值给elementData public ArrayList(Collection<?...),那么会从最后一个元素遍历,凑巧一下子找到了元素,这种情况下性能是和ArrayList中的get性能一样的;然后index为500000得时候,ArrayList和之前get性能基本一样,而LinkedList...对于前者,仍然只需要新建一个Node和改变前后指针指向,而后者会发生数组复制,将原数组所有元素拷贝到自己从第二个位开始,长度为size的对应位置,然后将入参赋值给0号位置,出了数组复制,还可能出现扩容,
Set和List两个类继承于它。Set中不能包含重复的元素,也没有顺序来存放。而List是一个有序的集合,可以包含重复的元素。 而Map又是另一个接口,它和Collection接口没有关系。...今天我们来专门看看ArrayList的源码。 成员变量 首先我们来看看ArrayList的成员变量: ? 可以看到主要的几个成员变量如上(跟进继承的父类,父父类直到根父类都没有成员变量)。...如果没有元素,那么直接赋值为EMPTY_ELEMENTDATA。 至此三个构造方法就已经分析完了,基本上没有什么难度。 常见方法 接下来我们来分析一些ArrayList的常见方法。...首先判断我们传入的object是否为空,如果为空,那么就for循环找到第一个数组中值为null的元素,调用fastRemove()方法,我们去看看: ?...我们来看看代码,首先是对传入对象的判空。如果对象为空,还是一样的,for循环来查找elementData中第一个为null的元素,然后返回下标。
那如何可以避免上述异常的出现?即我们希望当我们向集合中添加了不符合类型要求的对象时,编译器能直接给我们报错,而不是在程序运行后才产生异常。这个时候便可以使用泛型了。...我们通过 arrayString 对象和 arrayInteger 对象的 getClass() 方法获取它们的类信息并比较,发现结果为true。...我们可以调用 get() 方法从集合中获取元素,并赋值给集合中的最高父类 Number (即 的上界)。 (1)上界通配符 集合 dest 的元素后只能赋值给 Object 对象,而不能赋值给其下界类型 T;我们不能向 集合 src 中添加任何类型的对象,除了 null。
操作符设置 name 的值,那么调用方可以随意为 name 设置任何值,这就违反了 name 变量的非空约束。...相反,我们应该找到一些方法,将对象的值赋值到内部成员变量中,比如使用 System.arraycopy() 方法将元素中一个数组复制到另一个数组中。...比如,ArrayList 类的 clone() 方法的 Javadoc 描述如下: /** * Returns a shallow copy of this ArrayList instance...解决方案是为我们自己定义的对象(上例中的 Person 类)实现 clone() 方法。...•从 getter 返回一个克隆的对象。•在 setter 中分配一个克隆的对象。
比如在Java中,我们经常使用的ArrayList类。 它为我们提供了一种方便的方式来管理和操作一个动态数组,但是你是否曾经停下来3思考过它是如何工作的呢?它的内部机制是什么?...ArrayList类来存储、添加、修改和移除元素。...使用另一个for循环,遍历data中的所有元素并打印它们。 修改data中索引为1的元素为字符串"了不起"。 再次使用for循环,打印修改后的data中的所有元素。...最后,使用remove方法从data中移除索引为1的元素(即"了不起"),并打印移除元素后的data。 这段代码展示了ArrayList的基本操作,包括添加元素、获取元素、修改元素和移除元素。...然后,它会通过位运算将数组长度扩大1.5倍,并将结果赋值给newCapacity。接着,如果newCapacity小于minCapacity,它会将newCapacity设置为minCapacity。
在开始介绍之前,我们要先介绍一下ArrayList类中的一些属性。 /** * *默认初始容量。...任何空的数组且满足elementData == * DEFAULTCAPACITY_EMPTY_ELEMENTDATA * 将在添加第一个元素时扩展为DEFAULT_CAPACITY...DEFAULTCAPACITY_EMPTY_ELEMENTDATA的时候,数组就会被扩容为10; 那么接下来我们看一下ArrayList的三个构造方法。...,将elementData赋值为空数组 elementData = EMPTY_ELEMENTDATA; } } ArrayList的扩容机制 我们向ArrayList...这里的判断是因为我们有两种不同的构造函数,一个是无参,另一个是有参,无参构造函数在添加数据的时候会自动将数组扩容为10。
为了安全起见,在调用时需要把block先赋值给本地变量,以防止block改变。若不这么操作,即使先判断了block不为空,调用前,一旦另一个线程把block置为空,程序会crash。...从weak表中获取废弃对象的地址为键值的记录 b. 将包含在记录中的所有附有 weak修饰符变量量的地址,赋值为 nil c. 将weak表中该记录删除 d....,能否向运行时创建的类添加实例变量?...不能向编译后得到的类增加实例变量 可以向运行时创建的类添加实例变量 原因: 编译后的类已经注册在runtime中,类结构体中objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已确定...所以不能向存在的类中增加实例变量。
3ArrayList解析 我们提到数组的特点是大小固定,ArrayList的底层是基于数组来实现容量的大小动态变化的,那我们一起来结合源码看看,是如何实现这一功能的。...我们找到java.util.ArrayList包查看代码。并通过注释的方式,一起来揭开面纱。...也就是说,这是一个共享的空数组实例,通过与默认的空数组区分开,好处是,添加元素时知道该 elementData 从空的构造函数还是有参构造函数被初始化的。以便确认如何扩容。...那就赋值空数组。...= 0) { // 这里做了优化,如果也是一个ArrayList集合直接赋值即可 if (c.getClass() == ArrayList.class) {
所有值类型的数据都无法为null的,声明后必须赋以初值;引用类型才允许为null。 不过这里我们可以看一下可空类型 可空类型 可空类型可以表示基础类型的所有值,另外还可以表示 null 值。...3.比较可空类型时,只要一个操作数为null,比较结果就为false。 ? 值类型和引用类型在赋值(或者说复制)的时候也是有区别的。...托管堆是进程可用4GB的另一个区域,我们用一个例子了解托管堆的工作原理和为引用数据类型分配内存。假设我们有一个Cat类。...代码声明了一个ArrayList对象,向ArrayList中添加两个数字1,2;然后使用foreach将ArrayList中的元素打印到控制台。...在这个过程中会发生两次装箱操作和两次拆箱操作,在向ArrayList中添加int类型元素时会发生装箱,在使用foreach枚举ArrayList中的int类型元素时会发生拆箱操作,将object类型转换成
ArrayList扩容调用的是Array.copyof函数,把老数组遍历赋值给新数组返回。 说说ArrayList常见方法的时间复杂度?...2.1 用Debug分析一个元素是如何add进ArrayList 编写测试用例,打上断点: ? 先分析构造函数如何初始化,关键步骤如下: ?...>> 1); //新容量小于最小容量,则赋值为最小容量,此时newCapacity等于0,minCapacity为10 if (newCapacity - minCapacity...2.2 用Debug分析如何通过数组下标获取ArrayList元素 打上断点,debug: ? 首先进行范围检查,而后返回元素 ? ?...进入remove方法: public boolean remove(Object o) { //如果对象为空,则遍历ArrayList集合 if (o == null) {
2,ArrayList构造方法 下面是查看API中构造方法 构造方法 2.1,无参构造方法 我们看源码中的无参构造方法: 无参构造,使用默认的size为10的空数组,在构造方法中没有对数组长度进行设置,...无参构造 里面是一个赋值操作,右边是一个空容量数组,左边则是存储数据的容器,以下是参照源码分析; //默认空容量数组,长度为0 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA...public boolean isEmpty() 5,常见面试题(精华) 5.1,ArrayList是如何扩容的?...频繁扩容导致性能下降,如何处理?...我们来测试一下,先准备一个线程任务类: 然后定义测试类,对任务类进行测试: 我们来看结果: 可以看到会报异常错误,有的线程还是为null,这说明ArrayList线程是不安全的。
this.elementData = EMPTY_ELEMENTDATA; } } 上述代码,首先将传入的集合转换成数组并赋值给 elementData,再将elementData...的长度赋值为size,然后判断是否为0,如果不为0,则判断 elementData 的 class 类型是否为 Object[],不是的话则做一次转换。...如果为0则将EMPTY_ELEMENTDAT赋值给elementData。...[size++] = e; return true; } 向ArrayList添加一个元素 package com.kc.demo; import java.util.ArrayList...看完构造方法、添加方法、扩容方法之后,new ArrayList(),elementData赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,new ArrayList(0),会将
领取专属 10元无门槛券
手把手带您无忧上云