ArrayList类中的属性
java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
@java.io.Serial
private static final long serialVersionUID = 8683452581122892189L;
/**
* DEFAULT_CAPACITY表示集合默认的初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空数组EMPTY_ELEMENTDATA,在使用有参构造方法时传递0进去,创建ArrayList集合时就会使用这个数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
默认容量的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 集合中真实存储元素的数组elementData
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 集合中元素的个数
* @serial
*/
private int size; //int类型的成员变量初始值为0
ArrayList类中的构造方法
- 无参构造方法:相当于内部创建了一个空的数组
java
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
//将成员变量中的 默认容量的空数组 赋值给 真实存储元素的数组
//this.elementData = {}
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 有参构造方法分析:内部创建了一个指定长度的数组 并赋值给了elementData
main方法:
java
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList(20); //创建一个指定初始容量为20的ArrayList集合
}
}
- 有参构造方法源码:
java
public ArrayList(int initialCapacity) { // initialCapacity=20
if (initialCapacity > 0) {
//内部创建了一个长度为20的数组,并赋值给this.elementData
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果传递的是0,就将空数组EMPTY_ELEMENTDATA 赋值给 this.elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {
//如果在构造方法中传递负数,就会抛出如下异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
无参构造方法创建的对象:添加方法
第一次添加数据:
在main方法中:
java
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1); //添加第一个元素进去
}
}
第一次添加数据的流程:
- 一级一级的调用下去
java
public boolean add(E e) {
//确定内部数组的容量 0 + 1
ensureCapacityInternal(size + 1); // Increments modCount!!
//将要添加的元素 添加 到数组中有数据的下一个位置
elementData[size++] = e;
return true;
}
-------------------------------------------------------------------------------------
//minCapacity:是ArrayList集合需要的最小容量(暂时这样理解)
private void ensureCapacityInternal(int minCapacity) { // minCapacity=1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // {}=={}
// 10 1
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//通过得到的minCapacity去确定数组的长度
ensureExplicitCapacity(minCapacity);
}
-------------------------------------------------------------------------------------
private void ensureExplicitCapacity(int minCapacity) { //minCapacity=10
modCount++; //记录对当前集合操作的次数
// 集合需要的最小容量minCapacity 大于 elementData数组的长度时就会扩容(也就是当集合内部的数组不够存储元素时才会扩容) 10-0
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
-------------------------------------------------------------------------------------
private void grow(int minCapacity) { //minCapacity=10
int oldCapacity = this.elementData.length; //原来的数组长度为0
int newCapacity = oldCapacity + (oldCapacity >> 1); //新的容量0
if (newCapacity - minCapacity < 0) { //0 - 10 < 0,执行
newCapacity = minCapacity; //newCapacity = 10
}
if (newCapacity - 2147483639 > 0) { //10 - 21亿 < 0
newCapacity = hugeCapacity(minCapacity);
}
//将空数组{} 复制到 一个长度为 10的新数组中,然后将新的数组赋值给elementData。此时size还是0
this.elementData = Arrays.copyOf(this.elementData, newCapacity);
}
//然后回去继续向下执行,执行的就是向数组中添加数据的操作
public boolean add(E e) {
//确定内部数组的容量 0 + 1
ensureCapacityInternal(size + 1); // 0 + 1
//将要添加的元素 添加 到数组中有数据的下一个位置
elementData[size++] = e; //elementData={e,,,,,,,,,} 数组的length=10,size=1
return true;
}
第二次添加元素:
- 第二次添加元素也还不会扩容
main方法中:
java
package string.demo;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2); //第二次添加数据
}
}
第二次添加元素的执行流程:
java
public boolean add(E e) {
//确定内部数组的容量 1 + 1 = 2
ensureCapacityInternal(size + 1); // Increments modCount!!
//将要添加的元素 添加 到数组中有数据的下一个位置
elementData[size++] = e;
return true;
}
-------------------------------------------------------------------------------------
private void ensureCapacityInternal(int minCapacity) { // minCapacity=2
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // {1,,,...}!={} 不执行语句体
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确定数组的容量
ensureExplicitCapacity(minCapacity);
}
-------------------------------------------------------------------------------------
private void ensureExplicitCapacity(int minCapacity) { minCapacity=2
modCount++; //记录对当前集合操作的次数 modCount=modCount + 1 (modCount=2)
// 集合需要的最小容量minCapacity 大于 elementData数组的长度时就会扩容 2 - 10 < 0:不执行if语句体,也就是不扩容
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
-----------------------------------------------------------------------------------------
//然后回去继续向下执行,执行的就是向数组中添加数据的操作
public boolean add(E e) {
//确定内部的容量 0 + 1
ensureCapacityInternal(size + 1); // Increments modCount!!
//将要添加的元素 添加 到数组中有数据的下一个位置
elementData[size++] = e; // {1,2,.....} 此时:数组length = 10,元素个size=2
return true;
}
第十一次添加元素
- 此时ArrayList集合内部的数组不够存储新元素了,会扩容
java
public boolean add(E e) {
//此时elementData数组:{1,2,3,4,5,6,7,8,9,10} size = 10 , length = 10
ensureCapacityInternal(size + 1); // 10 + 1 =11
//将要添加的元素 添加 到数组中有数据的下一个位置
elementData[size++] = e;
return true;
}
-------------------------------------------------------------------------------------
private void ensureCapacityInternal(int minCapacity) { minCapacity = 11
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确定数组的容量
ensureExplicitCapacity(minCapacity); //11
}
-------------------------------------------------------------------------------------
private void ensureExplicitCapacity(int minCapacity) { minCapacity=11
modCount++; //记录对当前集合操作的次数 modCount=modCount + 1 (modCount=11)
// minCapacity 大于 elementData数组的长度时就会扩容 11-10 > 0:扩容
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
-------------------------------------------------------------------------------------
private void grow(int minCapacity) { //minCapacity=11
//oldCapacity = 10
int oldCapacity = this.elementData.length; //获取原容量的长度
//新的容量newCapacity是在原容量的基础上扩容1.5倍
//newCapacity = 10 + 5
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) { //15 - 11 > 0,不执行
newCapacity = minCapacity;
}
if (newCapacity - 2147483639 > 0) { //15 - 21亿 < 0,不执行
newCapacity = hugeCapacity(minCapacity);
}
//将{1,2,3,4,5,6,7,8,9,10}长度为10的数组 复制到 长度为 15的新数组中,然后将新的数组赋值给elementData。
this.elementData = Arrays.copyOf(this.elementData, newCapacity);
}
//再回去执行向数组中添加元素的逻辑
public boolean add(E e) {
//此时elementData数组:{1,2,3,4,5,6,7,8,9,10} size = 10 , length = 10
ensureCapacityInternal(size + 1); // 10 + 1 =11
//将要添加的元素 添加 到数组中有数据的下一个位置{1,2,3,4,5,6,7,8,9,10,11,,,,} 此时size=11 length=15
elementData[size++] = e;
return true;
}
有参构造方法创建的对象:添加方法
执行时调用的方法都和无参的一样
main方法:
java
package string.demo;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//elementData = {,,,,,} 长度为20的数组
ArrayList list2 = new ArrayList(20);
list2.add(1);
}
}
添加数据:
java
public boolean add(E e) {
//确定内部的容量 0 + 1
ensureCapacityInternal(size + 1); // Increments modCount!!
//将要添加的元素 添加 到数组中有数据的下一个位置
elementData[size++] = e;
return true;
}
-------------------------------------------------------------------------------------
private void ensureCapacityInternal(int minCapacity) { // minCapacity=1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // elementData != {}不执行
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确定数组的容量
ensureExplicitCapacity(minCapacity);
}
-------------------------------------------------------------------------------------
private void ensureExplicitCapacity(int minCapacity) { //minCapacity=1
modCount++; //记录对当前集合操作的次数
// 集合中的元素个数 等于数组的长度时才会扩容
if (minCapacity - elementData.length > 0) //1 - 20 < 0 不扩容
//扩容
grow(minCapacity);
}
-------------------------------------------------------------------------------------
private void grow(int minCapacity) {
int oldCapacity = this.elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //新的容量
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
if (newCapacity - 2147483639 > 0) {
newCapacity = hugeCapacity(minCapacity);
}
this.elementData = Arrays.copyOf(this.elementData, newCapacity);
}
ArrayList中的get方法
main方法:
java
package string.demo;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.get(0);
}
}
ArrayList中的get方法源码:
java
public E get(int index) {
//检查下标是否合法
Objects.checkIndex(index, size);
//通过下标获取数组中对应的元素
return elementData(index);
}
ArrayList中的set方法
main方法:
java
package string.demo;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.set(1,"aaa");
}
}
ArrayList中的set方法源码:
jdk17:
java
public E set(int index, E element) {
Objects.checkIndex(index, size); //检查下标是否合法
E oldValue = elementData(index); //获取原来下标对应的元素
elementData[index] = element; //将新的元素赋值给数组中对应的下标位置上
return oldValue; //返回原来的值
}
过去版本的jdk:
java
public E set(int index, E element) {
rangeCheck(index); //检查下标是否合法
E oldValue = elementData(index); //获取原来下标对应的元素
elementData[index] = element; //将新的元素赋值给数组中对应的下标位置上
return oldValue; //返回原来的值
}
ArrayList中的remove方法:
main方法:
java
package string.demo;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
list1.add(6);
list1.add(7);
list1.add(8);
list1.add(9);
list1.remove(3);
}
}
ArrayList中的remove方法源码:
过去版本的jdk:
java
public E remove(int index) { index=3
rangeCheck(index); //检查下标
modCount++; //记录修改当前集合的次数
E oldValue = elementData(index); //获取原来下标上的元素
//计算要移动的元素的个数{1,2,3,4,5,6,7,8,9}
//{1,2,3,5,6,7,8,9,null}
int numMoved = size - index - 1; //9 - 3 - 1 = 5
if (numMoved > 0)
System.arraycopy(elementData //原数组
, index+1 //开始移动的下标 5,6,7,8,9
, elementData //目标数组
, index, //开始的下标
numMoved //要移动的元素个数
);
//{1,2,3,5,6,7,8,9,null}
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
java
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
TreeSet的实现原理
在构造方法中创建了一个TreeMap实例
java
public TreeSet() {
this(new TreeMap<>());
}
在调用add方法时,本质上是调用了TreeMap的put方法
java
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
Hashset的实现原理
在其无参构造方法中实例化了一个HashMap实例
java
public HashSet() {
map = new HashMap<>();
}
在调用add方法的时候本质上是调用了HashMap的put方法
java
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
去除ArrayList集合中重复的元素
方式一:
会创建一个新的ArrayList集合
java
package collection;
import java.util.ArrayList;
public class ListTest {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(3);
list1.add(2);
list1.add(null);
list1.add("张三");
list1.add("李四");
list1.add(3);
System.out.println(list1);
//新创建一个List集合存储去重后的元素
ArrayList list2 = new ArrayList();
for (Object o : list1){ //遍历原来的集合
if (!list2.contains(o)){ //判断新的集合中是否包含list1中的元素,如果没有则1添加到list2中
list2.add(o);
}
}
System.out.println(list2);
}
}
运行结果:
[1, 3, 2, null, 张三, 李四, 3]
[1, 3, 2, null, 张三, 李四]
方式二
不创建新集合,使用选择排序的思想去除重复元素
java
package collection;
import java.util.ArrayList;
public class ListTest {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(3);
list1.add(2);
list1.add(2);
list1.add(2);
list1.add("张三");
list1.add("李四");
list1.add(3);
System.out.println(list1);
for (int i = 0;i < list1.size() - 1;i++){
for (int j = i + 1;j<list1.size();j++){
if (list1.get(i).equals(list1.get(j))){
list1.remove(j);
j--;
}
}
}
System.out.println(list1);
}
}