当前位置: 首页 > news >正文

Java基础16(集合框架 List ArrayList容器类 ArrayList底层源码解析及扩容机制)

目录

一、什么是集合?

二、集合接口

三、List集合

四、ArrayList容器类

1. 常用方法

1.1 增加

1.2 查找

int  size() 

E  get(int index) 

int indexOf(Object  c) 

boolean contains(Object  c)

boolean  isEmpty()

List  SubList(int fromindex,int  toIndex)

boolean equals(Object o)

1.3 修改

E set(int index, E element)

1.1.4 删除

 boolean removeAll(Collection c)--- 差集

boolean retainAll(Collection c)   --交集 

1.5 其他方法

clone()  

sort()

List和Array转换

2. 遍历

2.1 for循环

2.2 增强for循环

2.3 迭代器

Iterator的remove 

3. 初始化

无参初始化

有参初始化

五、ArrayList底层源码解析及扩容机制(⭐)

【特点】 

【数据结构】

扩容机制:

 总结:

小结


一、什么是集合?

        什么是集合?集合就是“由若干个确定的元素所构成的整体”,在程序中,一般代表保存若干个元素(数据)的某种容器类。在数学中,我们经常遇到集合的概念。例如:


●有限集合:

  • 一个班所有的同学构成的集合;
  • 一个网站所有的商品构成的集合;
  • ...

●无限集合:

  • 全体自然数集合:1,2,3,……
  • 有理数集合;
  • 实数集合;
  • ...


为什么要在计算机中引入集合呢?这是为了便于处理一组类似的数据,例如:

  • 计算所有同学的总成绩和平均成绩;
  • 列举所有的商品名称和价格;
  • ……


        在Java中,如果一个Java对象可以在内部持有(保存)若干其他Java对象,并对外提供访问接口,我们把这种Java对象的容器称为集合。很显然,Java的数组也可以看作是一种集合:

        既然Java提供了数组这种数据类型,可以充当集合,那么,我们为什么还需要其他集合类?这是因为数组有如下限制:

  • 长度开始时必须指定,且一旦指定,不能更改;即数组长度很难扩容
  • 保存的必须为同一类型的元素;
  • 数组的方法和功能不完善,查看数组中的具体长度,只有length属性知道内存大小,不知道具体的元素个数。 

public static void main(String[] args) {String[] menu = new String[10];menu[0] = "麻婆豆腐";menu[1] = "麻辣烫";menu[2] = "null";System.out.println(menu.length);//10ArrayList<String> arrayList = new ArrayList<String>();for (int i = 0; i < 3; i++) {arrayList.add("麻婆豆腐");arrayList.add("麻辣烫");arrayList.add("null");
//			arrayList.add( 12 );
//			arrayList.add( 12.23 );
//			arrayList.add( true );
//			arrayList.add(new Date());}System.out.println(arrayList);//[麻婆豆腐, 麻辣烫, null, 麻婆豆腐, 麻辣烫, null, 麻婆豆腐, 麻辣烫, null]System.out.println(arrayList.size());//9}


因此,我们需要各种不同类型的集合类来处理不同的数据,例如:

  • 可变大小的顺序链表;
  • 保证无重复元素的集合;
  • ...

二、集合接口

        Java标准库自带的java.util包提供了集合相关的接口和实现类:Collection接口,它是除Map接口外所有其他集合类的根接口。

Collection接口:collection接口是单例集合的跟接口,英文含义集中、收集

单列集合是将数据进行一个一个的存储,存储的是值

Java的java.util包主要提供了以下三种类型的集合:

  • List:一种有序列表的集合;
  • Set:一种保证没有重复元素的集合;
  • Map:一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map。

        Java集合的设计有几个特点:首先实现了接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayList,LinkedList等;其次支持泛型,我们可以限制在一个集合中只能放入同一种数据类型的元素,例如:

List<String> list = new ArrayList<>(); // 只能放入String类型

        最后,Java访问集合总是通过统一的方式——迭代器(Iterator)来实现,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。
        另外,由于Java的集合设计非常久远,中间经历过大规模改进,我们要注意到有一小部分集合类是遗留类,不应该继续使用:

  • Hashtable:一种线程安全的Map实现;
  • Vector:一种线程安全的List实现;
  • Stack:基于Vector实现的LIFO的栈;

三、List集合

        在集合类中,List是最基础的一种集合:它是一种有序列表。List的行为和数组几乎完全相同:List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,List的索引和数组一样,从0开始。

List的特点:

  • 元素有序的-即存入顺序和取出顺序是一致的(注意区分有序和排序)
  • 是可以重复的

我们观察List<E>接口,可以看到几个主要的接口方法:

  • 在末尾添加一个元素:boolean add(E e)
  • 在指定索引添加一个元素:boolean add(int index, E e)
  • 删除指定索引的元素:E remove(int index)
  • 删除某个元素:boolean remove(Object e)
  • 获取指定索引的元素:E get(int index)
  • 获取链表大小(包含元素的个数):int size()

四、ArrayList容器类

1. 常用方法

方法名说明
public boolean add(E e)将指定的参数元素追加到集合的末尾
public void add(int index ,E e)在集合的指定位置添加指定的元素(插入元素)
public void addAll(E object)用于将指定集合中所有元素添加到当前集合中
boolean addAll(int  index,Collection<? extends E> c)  从指定的位置开始,将指定 collection 中的所有元素插入到此列表中
方法名说明
public boolean remove(Object o)删除指定的元素,成功则返回true
public E remove(int index)删除指定索引位置的元素,返回被删除的元素
public E set(int index,E e)修改指定索引位置的元素,返回修改前的元素
public E get(int index)获取指定索引对应的元素
public int size()获取结合中元素个数

1.1 增加

  • boolean add(E,e)添加指定元素到集合尾部
    //泛型使用的目的:帮助我们建立类型安全的集合,添加元素的时候传入实际的类型//泛型使用好处: 代码可读性增强,程序更安全//泛型集合--泛型必须是引用数据类型ArrayList<String> arrayList =new ArrayList<String>();//add(元素)添加元素到集合的尾部boolean b = arrayList.add("z张三");System.out.println("添加z张三是否成功:"+b);arrayList.add("l李四");arrayList.add("w王五");arrayList.add("r任六");arrayList.add("z张三");System.out.println(arrayList);运行结果:
    添加z张三是否成功:true
    [z张三, l李四, w王五, r任六, z张三]
  •  void add(int index, E element)添加新元素到集合指定的下标位置
    //add(int index, E element)添加新元素到集合指定的下标位置arrayList.add(0,"孙二娘");arrayList.add(3,"刘备");System.out.println(arrayList);//[孙二娘, z张三, l李四, 刘备, w王五, r任六, z张三]
    
  • boolean addAll(Collection<? extends E> c)  添加集合C 内所有元素到当前集合 
    //使用Arrays.asList方法快速生成一个List集合//Arrays.asList返回的不是java.util下的ArrayListList<String> list = Arrays.asList("李白","高适","李清照","杜甫");//boolean addAll(Collection<? extends E> c)  添加集合C 内所有元素到当前集合arrayList.addAll(list);System.out.println(arrayList);//[孙二娘, z张三, l李四, 刘备, w王五, r任六, z张三, 李白, 高适, 李清照, 杜甫]
  • boolean addAll(int  index,Collection<? extends E> c)  从指定的位置开始,将指定 collection 中的所有元素插入到此列表中
    //boolean addAll(Collection<? extends E> c)  添加集合C 内所有元素到当前集合arrayList.addAll(list);System.out.println(arrayList);//[李白, 高适, 李清照, 杜甫, 孙二娘, z张三, l李四, 刘备, w王五, r任六, z张三, 李白, 高适, 李清照, 杜甫]
    

1.2 查找

int  size() 

查集合的长度,具体的元素个数

ArrayList<String> arrayList = new ArrayList<String>();arrayList.addAll(Arrays.asList("李白","高适","李清照","杜甫"));//1.获取元素的个数int size = arrayList.size();System.out.println("元素的个数为:"+size);//元素的个数为:4
E  get(int index) 

获取下标中的元素

//2.E get(int index)获取下标中的元素,System.out.println("首元素:"+arrayList.get(0));//首元素:李白System.out.println("尾元素:"+arrayList.get(arrayList.size()-1));//尾元素:杜甫
int indexOf(Object  c) 

查找找到指定元素的下标位置,如果不存在,则返回数组-1 

//3.int indexOf(Object  c) 查找找到指定元素的下标位置,如果不存在,则返回数组-1int index =arrayList.indexOf("高适1");System.out.println("高适所对应的下标位置:"+index);//高适所对应的下标位置:-1
boolean contains(Object  c)

判断集合中是否存在元素

//4.boolean contains(Object  c)System.out.println("高适是否存在:"+arrayList.contains("高适1"));//高适是否存在:false
boolean  isEmpty()

判断集合是否为空

 //5.判断集合是否为空boolean  isEmpty()System.out.println("集合是否为空:"+arrayList.isEmpty());//集合是否为空:false
List<E>  SubList(int fromindex,int  toIndex)

        subList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段、可以理解为截取一个集合中的部分元素,他的返回值也是一个List。

		//6.List<E>  SubList(int fromindex,int  toIndex)List<String> subList = arrayList.subList(0, 3);System.out.println(subList);//[李白, 高适, 李清照]
boolean equals(Object o)

  ArrayList.equals(Object obj) 方法比较的是两个列表(ArrayList或其它类型的List)的内容是否相等,即两个列表的大小必须相同,并且在相同索引位置上的元素也必须相等。

    //7.equals()boolean bequals = arrayList.equals(subList);System.out.println(bequals);//false

1.3 修改

E set(int index, E element)

修改指定索引位置的元素,返回修改前的元素

ArrayList<String> arrayList = new ArrayList<String>();arrayList.addAll(Arrays.asList("李白", "高适", "李清照", "杜甫"));System.out.println("原集合的内容:" + arrayList);// 修改指定下标的元素String result = arrayList.set(0, "王维");System.out.println("修改的元素:" + result);System.out.println("修改后的集合:" + arrayList);

1.1.4 删除

  • E remove(int index):根据指定索引删除元素,并把删除的元素返回 如果下标超过集合的最大下标,输出IndexOutOfBoundsException
// E remove(int index):根据指定索引删除元素,并把删除的元素返回String oldValue = arrayList.remove(0);System.out.println("删除的下标为0的元素为:" + oldValue);//删除的下标为0的元素为:李白
  • boolean remove(Object o):从集合中删除指定的元素,删除一个就返回 如果查到或者删除1个就返回,并不是删除所有相同的集合元素
//boolean remove(Object o):从集合中删除指定的元素,删除一个就返回boolean b = arrayList.remove("高适");System.out.println("删除元素高适是否成功:" + b);//删除元素高适是否成功:true
  • void clear():删除集合中的所有元素,此集合仍旧存在,集合元素长度变0
		//清空元素arrayList.clear();System.out.println(arrayList.size());//0
 boolean removeAll(Collection<?> c)--- 差集

从集合中删除一个指定的集合元素:
删除A集合中存在B集合中的所有相同的元素,如果有删除返回True

ArrayList<String> list1 = new ArrayList<String>();list1.addAll(Arrays.asList("周杰伦", "郭德刚", "张学友", "于适"));ArrayList<String> list2 = new ArrayList<String>();list2.addAll(Arrays.asList("周杰伦", "王一博", "张学友", "吴磊"));// boolean removeAll(Collection<?> c)--- 差集boolean b = list1.removeAll(list2);System.out.println(b);//trueSystem.out.println(list1);//[郭德刚, 于适]System.out.println(list2);//[周杰伦, 王一博, 张学友, 吴磊]
boolean retainAll(Collection<?> c)   --交集 

保留集合A和集合B中相同的元素,删除不同的元素,谁调用操作的是就是谁

ArrayList<String> list1 = new ArrayList<String>();list1.addAll(Arrays.asList("周杰伦", "郭德刚", "张学友", "于适"));ArrayList<String> list2 = new ArrayList<String>();list2.addAll(Arrays.asList("周杰伦", "王一博", "张学友", "吴磊"));//		boolean retainAll(Collection<?> c)   --交集boolean result=list1.retainAll(list2);System.out.println(result);//trueSystem.out.println(list1);//[周杰伦, 张学友]System.out.println(list2);//[周杰伦, 王一博, 张学友, 吴磊]

1.5 其他方法

clone()  

克隆一个集合,得到的一个长度,个数,内容,顺序完全一致的集合,单是赋值了一份

ArrayList<String> list1 = new ArrayList<String>();list1.addAll(Arrays.asList("z周杰伦", "g郭德刚", "d张学友", "y于适"));System.out.println("原集合:" + list1);//原集合:[z周杰伦, g郭德刚, d张学友, y于适]// list.clone() 克隆一个集合,得到的一个长度,个数,内容,顺序完全一致的集合,单是赋值了一份ArrayList<String> cloneList = (ArrayList<String>) list1.clone();System.out.println("克隆的集合:" + cloneList);//克隆的集合:[z周杰伦, g郭德刚, d张学友, y于适]
sort()

对list中的内容进行排序,需要自定义排序规则

// list.sort() 对list中的内容进行排序,需要自定义排序规则list1.sort(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}});System.out.println(list1);//[d张学友, g郭德刚, y于适, z周杰伦]

List和Array转换

  • object[] toArray()直接返回一个Object[]数组:
// object[] toArray()Object[] objects = list1.toArray();System.out.println("objects:"+Arrays.toString(objects));//objects:[d张学友, g郭德刚, y于适, z周杰伦]
  •  T[] toArray(T[] a)传入一个类型相同的Array,List内部自动把元素复制到传入的Array中:
// T[] toArray(T[] a)以较长的长度为String[] arrayList = list1.toArray(new String[0]);System.out.println("arrayList:"+Arrays.toString(arrayList));//arrayList:[d张学友, g郭德刚, y于适, z周杰伦]

2. 遍历

2.1 for循环

和数组类型,我们要遍历一个List,完全可以用for循环根据索引配合get(int)方法遍历:

        

ArrayList<String> arrayList = new ArrayList<String>();arrayList.addAll(Arrays.asList("李白", "高适", "李清照", "杜甫"));// 1.遍历for (int i = 0; i < arrayList.size(); i++) {System.out.print(arrayList.get(i) + " ");}
运行结果:李白 高适 李清照 杜甫 

2.2 增强for循环

        只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用,原因就在于Iterable接口定义了一个Iterator<E> iterator()方法,强迫集合类必须返回一个Iterator实例。

ArrayList<String> arrayList = new ArrayList<String>();arrayList.addAll(Arrays.asList("李白", "高适", "李清照", "杜甫"));// 2.for eachfor (String str : arrayList) {System.out.print(str + "_");}
运行结果:李白_高适_李清照_杜甫_

2.3 迭代器

        是访问数据的模型,主要用来遍历Colllection集合。使用迭代器Iterator来访问List。Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。Iterator对象知道如何遍历一个List,并且不同的List类型,返回的Iterator对象实现也是不同的,但总是具有最高的访问效率。

Iterator对象有两个方法:

  • boolean hasNext()判断是否有下一个元素,
  • E next()返回下一个元素。

因此,使用Iterator遍历List代码如下:

// 3.迭代器// 3.1 获取当前集合的“普通的”迭代器对象Iterator<String> it = arrayList.iterator();// 通过迭代器对象判断是否存在下一个元素while(it.hasNext()) {System.out.print(it.next()+" ");//获取下一个元素}System.out.println();运行结果:李白 高适 李清照 杜甫 

使用List迭代器(ListIterator)遍历:

        继承了Iterator,是List集合派系所特有的迭代器,遍历方式与Iterator遍历类似,但也有其特殊之处:它内部含有hasPrevious()方法和previous()方法,可以配合使用进行倒序遍历,前提是必须要先正序遍历让指针移至最后,否则倒叙遍历没有效果

//3.2获取当前集合的"List迭代器"//从指定的下标开始正序遍历元素ListIterator<String> listIt = arrayList.listIterator(0);while(listIt.hasNext()) {System.out.print(listIt.next()+" ");//获取下一个元素}System.out.println();//逆序遍历集合ListIterator<String> listIt1 = arrayList.listIterator(4);while(listIt1.hasPrevious()) {System.out.print(listIt1.previous()+" ");}
运行结果:
李白 高适 李清照 杜甫 
杜甫 李清照 高适 李白 
Iterator的remove 
  • ①在使用迭代器的remove()操作时,会将更新后的modCount给expectedModCount,两者会得到同步,但是在调用集合的remove()方法后,两个不会进行同步,进而导致在checkForComodification()校验时不通过,抛出java.util.ConcurrentModificationException异常。
  • ②所以,在单线程下使用迭代器是没有问题的,但是在多线程下同时操作集合就不允许了,可以通过fail-fast快速失败机制,快速判断是否存在同时操作问题。因此,集合在多线程下使用是不安全的。
     Collection c2 = new ArrayList();c2.add("abc");c2.add("def");c2.add("xyz");Iterator it2 = c2.iterator();while(it2.hasNext()){Object o = it2.next();// 删除元素// 删除元素之后,集合的结构发生了变化,应该重新去获取迭代器// 但是,循环下一次的时候并没有重新获取迭代器,所以会出现异常:java.util.ConcurrentModificationException// 出异常根本原因是:集合中元素删除了,但是没有更新迭代器(迭代器不知道集合变化了)//c2.remove(o); // 直接通过集合去删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同。)// 使用迭代器来删除可以吗?// 迭代器去删除时,会自动更新迭代器,并且更新集合(删除集合中的元素)。it2.remove(); // 删除的一定是迭代器指向的当前元素。System.out.println(o);}System.out.println(c2.size()); //0
    

3. 初始化

无参初始化

  • 无参构造初始化:内部数组初始化成空数组
//		1.无参构造初始化:内部数组初始化成空数组ArrayList<String> list1 =new ArrayList<String>();

有参初始化

  • ArrayList(int initialCapacity)初始化成长度为指定容量的Object[]数组
//		2.ArrayList(int initialCapacity)初始化成长度为指定容量的Object[]数组ArrayList<String> list2 =new ArrayList<String>(10);System.out.println(list2.size());//0
  • ArrayList(Collection<? extends E> c) 内部的数组被集合进行初始化
    //		3.ArrayList(Collection<? extends E> c) 内部的数组被集合进行初始化ArrayList<String> list3=new ArrayList<String>(Arrays.asList("abc","def"));System.out.println(list3);//[abc, def]

五、ArrayList底层源码解析及扩容机制(⭐)

List的主要实现类,底层使用Object[]存储,适用于频繁的查找工作,线程不安全。

【特点】 

  • 增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素位置;    
  • 查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。

【数据结构】

底层是基于数组实现的,随着元素的增加而动态扩容

使用场景:适合数据连续性遍历,读多写少的场景


		ArrayList<String> list = new ArrayList<String>(0);//第一次往无参构造的数组list中添加元素的时候,数组首次扩容10list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");list.add("zkt");//...//后面每次容量不足时扩容为容量的1.5倍list.add("张kt");System.out.println(list);//[zkt, zkt, zkt, zkt, zkt, zkt, zkt, zkt, zkt, zkt, 张kt]list.addAll(Arrays.asList("张三","李四","王五"));

扩容机制:

  1. 当需要添加元素时,ArrayList会先判断当前容量是否足够,如果不足够,则需要进行扩容操作。
  2. 扩容操作的第一步是调用方法,该方法用于确定扩容后的最小容量。
  3. 如果ArrayList为空,则选择默认容量和传入容量的较大值作为新容量。
  4. 然后调用ensureExplicitCapacity方法,该方法用于判断是否需要进行扩容操作
  5. ensureExplicitCapacity方法中,修改操作次数加1,然后判断如果需要的最小容量大于当前容量,则进行扩容操作。
  6. 扩容操作的第二步是调用grow方法,该方法用于计算新的容量并进行扩容。
  7. 首先获取当前的容量,然后计算新的容量,一般是原容量的1.5倍,并向上取整。
  8. 如果新容量小于所需的最小容量,则直接使用最小容量作为新容量。
  9. 如果新容量超过了最大容量限制,则使用最大容量作为新容量。
  10. 最后,通过调用Arrays.copyOf方法,将原始数组拷贝到新数组中,实现扩容。

底层代码:

属性:

private static final int DEFAULT_CAPACITY = 10;// 默认的初始容量是10
// 空元素数组
private static final Object[] EMPTY_ELEMENTDATA = {};//有参构造所创建
// 默认容量的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//无参构造所创建的
// 存储元素的数组 
transient Object[] elementData;//底层为Object类型的数组,存储的元素都在此。
// 集合容量
private int size;

构造方法 :

空参构造:

构造一个初始容量的空数组。

// 1.构造一个初始容量的空数组。
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

单参(int)构造:

如果参数initialCapacity大于0,给elementData 初始化为长度为initialCapacity的数组;若等于0,初始化为一个长度为0的数组;若小于0则抛出异常

// 2.构造具有指定初始容量的数组。
public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);}
}

单参(Collection)构造:

传入容器参数,将c容器内的内容赋值给elementData,如果c无内容,则将一个空数组赋值给elementData,同时初始化数组长度size

//3.构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序
public ArrayList(Collection<? extends E> c) {// 将集合构造中的集合对象转成数组Object[] a = c.toArray();if ((size = a.length) != 0) {if (c.getClass() == ArrayList.class) {// 类型为ArrayList则直接赋值elementData = a;} else {//如果不一样,使用Arrays的copyOf方法进行元素的拷贝elementData = Arrays.copyOf(a, size, Object[].class);}} else {// replace with empty array.elementData = EMPTY_ELEMENTDATA;}
}

扩容源码:

进入add方法:

先执行ensureCapacityInternal确保数组elementData的容量足够,再将e添加进elementData

// 添加一个元素
public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;//e 操作对象;  elementData 底层操作的数组;size 默认大小0return true; 
}

进入ensureCapacityInternal方法:

其中calculateCapacity方法返回elementData需要的最低长度,该整数作为参数传入ensureExplicitCapacity方法中,用于判断是否需要扩容

// 1.主体函数
private void ensureCapacityInternal(int minCapacity) {// 对容量进行判断ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

进入calculateCapacity方法:

如果elementData 为空,则返回max(10, minCapacity),否则直接返回minCapacity

// 2.通过最小容量和默认容量求出较大值 (主要用于第一次扩容:如果是无参构造方式创建的数组对象,第一次添加元素的时候扩容到大小为10)
private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}

进入ensureExplicitCapacity方法:

如果elementData所需的最小长度大于目前实际的长度,则进入grow方法,进行扩容

// 3.判断是否需要进行扩容
private void ensureExplicitCapacity(int minCapacity) {// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)modCount++;// 判断当前最小容量是否大于数组长度if (minCapacity - elementData.length > 0)// 将计算出来的容量传递给核心扩容方法grow(minCapacity);
}

进入grow方法:

新数组长度=max(旧数组长度*1.5,数组所需最低长度)。如果新数组长度过大(大于Integer.MAX_VALUE - 8),进入hugeCapacity

// 4.核心扩容方法
private void grow(int minCapacity) {// 记录数组的实际长度int oldCapacity = elementData.length;// 核心扩容算法,原容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);// 判断新容量是否小于当前最小容量(第一次调用add方法必然小于)if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 判断新容量是否大于最大数组长度,如果if (newCapacity - MAX_ARRAY_SIZE > 0)// 条件满足就计算出一个超大容量newCapacity = hugeCapacity(minCapacity);// 调用数组工具类方法,创建一个新数组,将新数组的地址赋值给elementDataelementData = Arrays.copyOf(elementData, newCapacity);
}

进入hugeCapacity方法:

如果minCapacity溢出,抛出异常,否则返回一个合法的大的值

private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

 总结:

ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。(不是原数组,而是新数组然后给予数组对象地址)。

默认情况下,新的容量会是原容量的1.5倍。 新容量=旧容量右移一位(相当于除于2)在加上旧容量

  ArrayList 的底层是用动态数组来实现的我们初始化一个ArrayList 集合还没有添加元素时,其实它是个空数组,只有当我们添加第一个元素时,内部会调用扩容方法并返回最小容量10,也就是说ArrayList 初始化容量为10。 当前数组长度小于最小容量的长度时(前期容量是10,当添加第11个元素时就就扩容),便开始可以扩容了,ArrayList 扩容的真正计算是在一个grow()里面,新数组大小是旧数组的1.5倍,如果扩容后的新数组大小还是小于最小容量,那新数组的大小就是最小容量的大小,后面会调用一个Arrays.copyof方法,这个方法是真正实现扩容的步骤。

小结

Collection(I) 单例,一个一个的存储
       List集合(I)有序,可重复
               实现类:ArrayList:
                            所属的包: java.util.*
                            构造方法:

  •                                     ArrayList<E>()
  •                                     ArrayList<E>(int init)
  •                                     ArrayList<E>(Collection<E> c)

                              添加元素:

  •                                     boolean add(E)---添加元素到集合的末尾
  •                                     void  add(index,E)---添加元素到指定的位置
  •                                     boolean addAll(Collection c)---添加集合到集合的末尾
  •                                     boolean addAll(int index,Collection c)---添加集合到集合的末尾

                                 查看元素:  

  •                                     boolean contains(E) --- 是否包含某个元素
  •                                     int indexOf(Object c)--- 查找某个元素存在的下标位置
  •                                     E get(int index)---查找对应的下标元素
  •                                     boolean isEmpty()
  •                                     size() -- 查看元素个数
  •                                     List<E> subList(int for,int end)--截取集合
  •                                     boolean equals()--

                              修改元素:oldValue set(int index,E newElement)
                              删除元素:  

  •                                         E remove(int index)
  •                                         boolean remove(Object obj)
  •                                         clear()
  •                                         boolean removeAll(Collection c)
  •                                         boolean retainAll(Colleciont c)

                              其他方法:

  •                                   clone()克隆一个集合
  •                                   sort(Comparator) 排序
  •                                   Object[] toArray()
  •                                   T[] toArray(T[])

                                  
                              元素遍历:

  •                               for
  •                               foreach
  •                               普通迭代器 Iterator it = list.iterator();
  •                               hasNext
  •                               next
  •                               list迭代器:ListIterator it = list.listIterator(9);

                     
                      LinkedList
                      Vector
                      Stack
       Set集合(I)无序,不可重复
       Queue队列(I)先进先出

相关文章:

Java基础16(集合框架 List ArrayList容器类 ArrayList底层源码解析及扩容机制)

目录 一、什么是集合&#xff1f; 二、集合接口 三、List集合 四、ArrayList容器类 1. 常用方法 1.1 增加 1.2 查找 int size() E get(int index) int indexOf(Object c) boolean contains(Object c) boolean isEmpty() List SubList(int fromindex,int …...

数组:移除元素

参考资料&#xff1a;代码随想录 本题思路&#xff1a;通过快慢指针将两次循环减少到一次 class Solution {public int removeElement(int[] nums, int val) {//0 1 2 2 2 2 3int fast 0;int slow 0;while(fast < nums.length){if(nums[fast] ! val){nums[slow] nums[f…...

胡说八道(24.6.22)——通信杂谈(完结)

上回书说到雷达和香农的信息论&#xff0c;今天来进行完结。 数字幅值调制或幅值键控&#xff08;ASK&#xff09;调制方式是指载波信号的幅值随数字基带信号而变化&#xff0c;因此可以实现将基带信号搬移到载波频段。2ASK是利用代表数字信息0或1的基带矩形脉冲去键控一个连续…...

设计模式原则——里氏替换原则

设计模式原则 设计模式示例代码库地址&#xff1a; https://gitee.com/Jasonpupil/designPatterns 里氏替换原则 继承必须确保父类所拥有的性质在子类中依然成立 与开闭原则不同的是开闭原则可以改变父类原有的功能&#xff0c;里氏替换原则不能修改父类的原有的性质&#…...

详解 ClickHouse 的 SQL 操作

传统关系型数据库&#xff08;以 MySQL 为例&#xff09;的 SQL 语句&#xff0c;ClickHouse 基本都支持 一、插入 --语法&#xff1a; insert into table_name values(xxxxxx),(yyyyyyy),...;insert into table_name select xxxxx from table_name2 where yyyyy;二、更新和删…...

WPF与Winform,你的选择是?

概述 在桌面应用的发展历程中&#xff0c;Winform和WPF作为微软推出的两大框架&#xff0c;各自承载着不同的设计理念和技术特色。Winform以其稳定、成熟的技术基础&#xff0c;长期占据着企业级应用开发的重要地位。而WPF&#xff0c;作为后来者&#xff0c;以其现代化的UI设计…...

基于SpringBoot的实习管理系统设计与实现

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; SpringBoot框架&#xff0c;B/S模式 工具&#xff1a; MyEclipse&#xff0c;Tomcat 系统展示 …...

编程用什么电脑不卡的:深度解析与推荐

编程用什么电脑不卡的&#xff1a;深度解析与推荐 在编程的世界里&#xff0c;一台流畅不卡的电脑无疑是每个开发者的得力助手。然而&#xff0c;面对市场上琳琅满目的电脑品牌和型号&#xff0c;如何选择一台适合编程的电脑却成为了一个令人困惑的问题。本文将从四个方面、五…...

优先级队列模拟实现

目录 1.堆的概念 2.堆性质堆中的某个元素小于或大于他的左右孩子 3.小根堆实例 4.堆创建 4.1调整思路 4.2向下调整思路 4.3代码实现&#xff08;大根堆&#xff09; 5.堆的删除 6.堆的插入 7.常用接口 7.1PriorityQueue和PriorityBlockingQueue 1.堆的概念 如果有一…...

记一次服务器崩溃事件

今天在安装Jenkins的时候&#xff0c;进行到插件安装这一步&#xff0c;本来一切顺利&#xff0c;结果最后安装完成之后一直进不去网页&#xff0c;显示连接超时&#xff0c;网上搜索了一圈也没发现什么相似的情况&#xff0c;当我疑惑的时候回到Linux控制台&#xff0c;发现命…...

神经网络 #数据挖掘 #Python

神经网络是一种受生物神经元系统启发的人工计算模型&#xff0c;用于模仿人脑的学习和决策过程。它由大量互相连接的节点&#xff08;称为神经元&#xff09;组成&#xff0c;这些节点处理和传递信息。神经网络通常包含输入层、隐藏层&#xff08;可有多个&#xff09;和输出层…...

营销复盘秘籍,6步法让你的活动效果翻倍

在营销的世界中&#xff0c;每一次活动都是一次探险&#xff0c;而复盘就是探险后的宝藏图&#xff0c;指引我们发现问题、提炼经验、优化策略。 想要学习如何复盘&#xff0c;只要了解以下复盘六大步骤&#xff0c;即可不断总结&#xff0c;逐渐走向卓越。 第一步&#xff1…...

Linux下命令行文件创建删除、目录创建删除

在Linux命令行下&#xff0c;文件和目录的创建与删除是通过一系列基础命令完成的&#xff0c;这些命令对于日常的系统管理和文件操作至关重要。 下面将详细介绍这些命令的功能和使用方法。 普通文件的创建与删除 创建文件 touch命令&#xff1a;主要用于创建一个空文件&…...

数字排列问题

题目&#xff1a;有1、2、3、4个数字&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;都是多少&#xff1f; 代码&#xff1a; #include <stdio.h> int main() { int count 0; // 计数器&#xff0c;记录生成的三位数的数量 // 使用三个嵌套的fo…...

CentOS Linux 7系统中离线安装MySQL5.7步骤

预计数据文件存储目录为&#xff1a;/opt/mysql/data 1、文件下载&#xff1a; 安装文件下载链接&#xff1a;https://downloads.mysql.com/archives/community/ 2、检查当前系统是否安装过MySQL [rootcnic51 mysql]# rpm -qa|grep mariadb mariadb-libs-5.5.68-1.el7.x86_6…...

XSS跨站攻击漏洞

XSS跨站攻击漏洞 一 概述 1 XSS概述 xss全称为&#xff1a;Cross Site Scripting&#xff0c;指跨站攻击脚本&#xff0c;XSS漏洞发生在前端&#xff0c;攻击的是浏览器的解析引擎&#xff0c;XSS就是让攻击者的JavaScript代码在受害者的浏览器上执行。 XSS攻击者的目的就是…...

PMP到底值不值得考?

首先&#xff0c;咱们得明白PMP是个啥。 PMP&#xff0c;全称Project Management Professional&#xff0c;是美国项目管理协会PMI颁发的一个项目管理专业人士资格认证。 PMP证书在项目管理领域可是有着举足轻重的地位&#xff0c;与MBA、MPA并驾齐驱&#xff0c;被称为“全球…...

redis面试总结

redis的数据类型&#xff1f; string字符串&#xff1a;类似于java中Map<String,String>。存储字符串、JSON数据、验证码等。 Hash字典&#xff1a;类似java中Map<String, Map<Spring,String>>。比较适合存储对象数据。 List列表&#xff1a;类似java中Ma…...

大模型日报2024-06-24

大模型日报 2024-06-24 大模型资讯 大模型产品 AI快速生成专业播客 摘要: MakePodcast.io使用AI语音&#xff0c;只需提供脚本并选择声音&#xff0c;即可在几分钟内生成专业质量的播客。 Sherloq&#xff1a;SQL用户的AI协作仓库 摘要: Sherloq为SQL查询提供一站式管理&#x…...

深入理解计算机系统 CSAPP 练习题7.4

A:0x4004e8(-4)-50x4004df B:0x5...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...