Java集合系列(二)—— ArrayList

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

public class ArrayList extends AbstractList

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

  • 继承了 AbstractList ,实现了 List,提供了相关的增删改查功能。

  • 实现了 RandomAccess 接口,提供了随机访问的能力。

  • 实现了 Cloneable 接口,就是覆盖了 clone() 方法,使得 ArrayList 实例能被克隆。

  • 实现了 java.io.Serializable 接口,支持序列化。

ArrayList 主要的属性

================

//基础容量

private static final int DEFAULT_CAPACITY = 10;

//动态数组

transient Object[] elementData;

//空数组,供参数为集合或整数的构造器使用(详细请看下面的分析)

private static final Object[] EMPTY_ELEMENTDATA = {};

//空数组,供空参构造器使用(详细请看下面构造器的分析)

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//动态数组的长度(集合中元素的个数)

private int size;

//记录数组被修改的次数,用于防止fail-fast(具体看下面的分析)

protected transient int modCount = 0;

//动态数组的最大容纳量

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

光看属性似乎会有很多疑问或不解,大概对这些属性有个印象,结合下面的方法来看吧。

ArrayList 的构造器

==============

在 ArrayList 中,提供了三个构造器:空参构造器、参数为集合的构造器,参数为整数的构造器。

1. 空参构造器

public ArrayList() {

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

2. 参数为集合的构造器

public ArrayList(Collection<? extends E> c) {

//将集合转化为数组

elementData = c.toArray();

//判断c.toArray()的长度是否为0

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

// 检查c.toArray()是否为Object类型的数组,如果不是,则重新拷贝成一个Object类型的数组

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

//使用了Arrays这个工具类来进行浅复制,关于Arrays工具类的实现请移步相关文章

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

} else {

// 如果c.toArray()的长度为0,那么就直接使用预定义的空数组

this.elementData = EMPTY_ELEMENTDATA;

}

}

3. 参数为整数的构造器

public ArrayList(int initialCapacity) {//将合法的整型参数作为动态数组的初始化容量

//如果初始化容量大于0,则将参数作为数组的初始化容量

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);

}

}

常用的方法

=====

一、添加方法


ArrayList 中,提供了以下 4 个用于增加元素的方法:

1. boolean add(E e) —— 该方法用于向集合的末尾添加一个元素,返回一个布尔值,表示操作是否成功。

public boolean add(E e) {

//检查容量是否满足,具体分析看下面

ensureCapacityInternal(size + 1);

//将元素添加到数组的末尾

elementData[size++] = e;

//所有错误都在相应的方法里面处理了,所以此处返回true

return true;

}

private void ensureCapacityInternal(int minCapacity) {//参数为最小容量

//判断动态数组是否为预定义的空数组

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

//如果为空数组,则取出 默认容纳量(10)和最小容量两者之中的最大值

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

private void ensureExplicitCapacity(int minCapacity) {//参数为最小容量(就是上面方法那两者的最大值)

//修改计数器自增

modCount++;

//如果最小容量比动态数组的长度大,则进行扩容

if (minCapacity - elementData.length > 0)

//真正扩容的方法

grow(minCapacity);

}

private void grow(int minCapacity) {

//动态数组没添加元素之前元素的个数

int oldCapacity = elementData.length;

//新容量是没添加元素之前元素个数的1.5倍

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

//如果新容量小于最小需要的容纳量,则将最小需要的容纳量作为新容量

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

//如果新容量大于预定义的动态数组最大容纳量(详细看属性那里),则将最大容纳量作为新容量

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

//通过Arrays工具实现扩容

elementData = Arrays.copyOf(elementData, newCapacity);

}

private static int hugeCapacity(int minCapacity) {

//处理不合法参数

if (minCapacity < 0)

throw new OutOfMemoryError();

//如果需要的最小容纳量大于属性MAX_ARRAY_SIZE,则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE

//注:MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 (详细看属性的定义)

return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;

}

2. void add(int,E) —— 添加元素到指定的位置。

public void add(int index, E element) {

//处理不合法的参数index。其实就是检测数组的下标是否越界(详细的分析看后面)

rangeCheckForAdd(index);

//检测数组容量是否满足

ensureCapacityInternal(size + 1);

//这是一个native方法,死活不让我看源码!

//大致可以看出意思就是将index位置后面的所有元素往后移一位,空出index这个位置

System.arraycopy(elementData, index, elementData, index + 1, size - index);

//在数组的index这个位置存储元素的值,从而就实现了插入元素的效果

elementData[index] = element;

//size是集合中元素的个数,自增

size++;

}

private void rangeCheckForAdd(int index) {

//如果索引大于数组的长度或者小于0,则为不合法,抛出异常。

if (index > size || index < 0)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

3.  boolean addAll(Collection<? extend E>) —— 用于合并两个集合,意思就是将两个集合的元素放在源集合中,返回布尔值,表示操作是否成功

public boolean addAll(Collection<? extends E> c) {

//老套路,先将集合转换为数组

Object[] a = c.toArray();

//转换后数组的长度

int numNew = a.length;

//还是熟悉的配方,这是检测容量的那套方法,前面已经分析过了。

ensureCapacityInternal(size + numNew);

//该死的native方法

//大致的意思就是将数组的所有元素拷贝到集合的末尾

System.arraycopy(a, 0, elementData, size, numNew);

//更新长度

size += numNew;

return numNew != 0;

}

4.  boolean addAll(int,Collection<? extend E>) —— 将一个集合中所有元素插入到源集合中的 index 位置,结果返回布尔值,表示操作是否成功。

public boolean addAll(int index, Collection<? extends E> c) {

//检测参数是否合法。下标是否越界,在前面已经分析过了。

rangeCheckForAdd(index);

//一样的套路:转化为数组

Object[] a = c.toArray();

int numNew = a.length;

//考虑容量

ensureCapacityInternal(size + numNew); // Increments modCount

//本地代码无法查看,大致意思就是将index位置后面所有元素往后移,空出a.length个位置,然后将数组的所有元素插入到其中。

int numMoved = size - index;

if (numMoved > 0)

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

numMoved);

System.arraycopy(a, 0, elementData, index, numNew);

size += numNew;

return numNew != 0;

}

二、修改与获取元素的方法


ArrayList 中,修改与获取元素的方法分别只有一个,就是 getset

E set(int index, E element) —— 修改 index 位置的元素,结果返回被修改的元素。

public E set(int index, E element) {

//这个方法与前面判断越界的方法有所不同,具体看下面的分析

rangeCheck(index);

//获取旧的值

E oldValue = elementData(index);

//设置新的值

elementData[index] = element;

return oldValue;

}

private void rangeCheck(int index) {

//显然,不同之处就是少了一个条件:index < 0,这也就是意味着可以传递一个负数作为参数

if (index >= size)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

E get(int index) —— 获取 index 位置的元素。

public E get(int index) {

//检查下标是否越界

rangeCheck(index);

//返回数组index位置的元素

return elementData(index);

}

E elementData(int index) {

return (E) elementData[index];

}

三、删除元素的方法


ArrayList 中,提供了四个用于删除元素的方法。

1. E remove(int index) —— 移除集合中某个位置的元素,结果返回这个元素。

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;

}

2. boolean remove(Object o) —— 删除某个元素,如果集合中存在多个,则删除首次出现的,结果返回布尔值,表示是否成功。

public boolean remove(Object o) {

//分开两种情况考虑,一种为元素为null,另一种为非null,但是做法都是一致,遍历数组,拿到元素对应的索引,然后删除。

if (o == null) {

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

if (elementData[index] == null) {

fastRemove(index);

return true;

}

} else {

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

if (o.equals(elementData[index])) {

fastRemove(index);

return true;

}

}

return false;

}

private void fastRemove(int index) {//该方法用于删除某个位置的元素(其实逻辑跟前面那个remove方法是一致的!)

//修改计数器

modCount++;

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

}

3. boolean removeAll(Collection<? extend E> c) —— 在源集合中移除参数集合 c 所有的元素,意思就是求差集,返回布尔值表示是否成功

public boolean removeAll(Collection<?> c) {

//检查参数是否为null

Objects.requireNonNull©;

//批量删除

return batchRemove(c, false);

}

public static T requireNonNull(T obj) {

//如果参数为null,则抛出异常,否则返回本身

if (obj == null)

throw new NullPointerException();

return obj;

}

private boolean batchRemove(Collection<?> c, boolean complement) {

//获取动态数组

final Object[] elementData = this.elementData;

//r为循环计数器;

int r = 0, w = 0;

//修改标识

boolean modified = false;

try {

//遍历动态数组

for (; r < size; r++)

if (c.contains(elementData[r]) == complement)

elementData[w++] = elementData[r];

} finally {

// Preserve behavioral compatibility with AbstractCollection,

// even if c.contains() throws.

if (r != size) {

System.arraycopy(elementData, r,

elementData, w,

size - r);

w += size - r;

}

if (w != size) {

// clear to let GC do its work

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

elementData[i] = null;

modCount += size - w;

size = w;

modified = true;

}

}

return modified;

}

4. void clear() —— 清空集合。

public void clear() {

//修改计数器

modCount++;

//将所有元素置为null

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

elementData[i] = null;

//将长度置为0

size = 0;

}

最后

现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**

image
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
size - r;

}

if (w != size) {

// clear to let GC do its work

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

elementData[i] = null;

modCount += size - w;

size = w;

modified = true;

}

}

return modified;

}

4. void clear() —— 清空集合。

public void clear() {

//修改计数器

modCount++;

//将所有元素置为null

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

elementData[i] = null;

//将长度置为0

size = 0;

}

最后

现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**

[外链图片转存中…(img-fjK7jSDd-1714694587417)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
判断两个list集合里的对象某个属性值是否一样可以按照以下步骤进行: 1. 遍历第一个list集合,以该对象的某个属性值为key,将对象存入一个Map中。 2. 遍历第个list集合,以该对象的某个属性值为key,从Map中获取该key对应的对象,进行比较。 3. 如果两个对象的属性值相同,则说明两个list集合里的对象某个属性值是一样的。 下面是Java代码实现: ```java public boolean isSameProperty(List<Object> list1, List<Object> list2, String property) { Map<Object, Object> map = new HashMap<>(); for (Object obj : list1) { try { Field field = obj.getClass().getDeclaredField(property); field.setAccessible(true); Object key = field.get(obj); map.put(key, obj); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } for (Object obj : list2) { try { Field field = obj.getClass().getDeclaredField(property); field.setAccessible(true); Object key = field.get(obj); Object obj1 = map.get(key); if (obj1 != null) { if (obj.equals(obj1)) { return true; } } } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } return false; } ``` 使用示例: ```java List<User> list1 = new ArrayList<>(); list1.add(new User("张三", 20)); list1.add(new User("李四", 30)); List<User> list2 = new ArrayList<>(); list2.add(new User("王五", 20)); list2.add(new User("赵六", 30)); boolean isSameAge = isSameProperty(list1, list2, "age"); System.out.println("isSameAge: " + isSameAge); // true ``` 其中,User类实现了equals和hashCode方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值