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集合中的所有相同的元素,如果有删除返回TrueArrayList<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("张三","李四","王五"));
扩容机制:
- 当需要添加元素时,ArrayList会先判断当前容量是否足够,如果不足够,则需要进行扩容操作。
- 扩容操作的第一步是调用
方法,该方法用于确定扩容后的最小容量。
- 如果ArrayList为空,则选择默认容量和传入容量的较大值作为新容量。
- 然后调用
ensureExplicitCapacity
方法,该方法用于判断是否需要进行扩容操作。- 在
ensureExplicitCapacity
方法中,修改操作次数加1,然后判断如果需要的最小容量大于当前容量,则进行扩容操作。- 扩容操作的第二步是调用
grow
方法,该方法用于计算新的容量并进行扩容。- 首先获取当前的容量,然后计算新的容量,一般是原容量的1.5倍,并向上取整。
- 如果新容量小于所需的最小容量,则直接使用最小容量作为新容量。
- 如果新容量超过了最大容量限制,则使用最大容量作为新容量。
- 最后,通过调用
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底层源码解析及扩容机制)
目录 一、什么是集合? 二、集合接口 三、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 …...

数组:移除元素
参考资料:代码随想录 本题思路:通过快慢指针将两次循环减少到一次 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)——通信杂谈(完结)
上回书说到雷达和香农的信息论,今天来进行完结。 数字幅值调制或幅值键控(ASK)调制方式是指载波信号的幅值随数字基带信号而变化,因此可以实现将基带信号搬移到载波频段。2ASK是利用代表数字信息0或1的基带矩形脉冲去键控一个连续…...

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

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

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

基于SpringBoot的实习管理系统设计与实现
你好呀,我是计算机学姐码农小野!如果有相关需求,可以私信联系我。 开发语言: Java 数据库: MySQL 技术: SpringBoot框架,B/S模式 工具: MyEclipse,Tomcat 系统展示 …...

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

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

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

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

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

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

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

CentOS Linux 7系统中离线安装MySQL5.7步骤
预计数据文件存储目录为:/opt/mysql/data 1、文件下载: 安装文件下载链接: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全称为:Cross Site Scripting,指跨站攻击脚本,XSS漏洞发生在前端,攻击的是浏览器的解析引擎,XSS就是让攻击者的JavaScript代码在受害者的浏览器上执行。 XSS攻击者的目的就是…...

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

redis面试总结
redis的数据类型? string字符串:类似于java中Map<String,String>。存储字符串、JSON数据、验证码等。 Hash字典:类似java中Map<String, Map<Spring,String>>。比较适合存储对象数据。 List列表:类似java中Ma…...

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

深入理解计算机系统 CSAPP 练习题7.4
A:0x4004e8(-4)-50x4004df B:0x5...

摘苹果-第13届蓝桥杯省赛Python真题精选
[导读]:超平老师的Scratch蓝桥杯真题解读系列在推出之后,受到了广大老师和家长的好评,非常感谢各位的认可和厚爱。作为回馈,超平老师计划推出《Python蓝桥杯真题解析100讲》,这是解读系列的第88讲。 摘苹果࿰…...

开源项目推荐-vue2+element+axios 个人财务管理系统
文章目录 financialmanagement项目简介项目特色项目预览卫星的实现方式:首次进入卫星效果的实现方式:卫星跟随鼠标滑动的随机效果实现方式:环境准备项目启动项目部署项目地址 financialmanagement 项目简介 vue2elementaxios 个人财务管理系…...

手机数据如何恢复?11 款最佳安卓手机恢复软件
媒体可能由于各种原因而从您的设备中删除,可能是意外或病毒攻击。 在这些情况下,照片恢复应用程序是唯一的解决方案。理想的照片恢复应用程序取决于各种因素,例如存储设备的损坏程度、删除照片后的持续时间以及应用程序使用的恢复算法的有效性…...

大语言模型千问2的web搭建(streamlit)
Qwen2的web搭建(streamlit) 千问2前段时间发布了,个人觉得千问系列是我用过最好的中文开源大模型,所以这里基于streamlit进行一个千问2的web搭建,来进行模型的测试 一、硬件要求 该文档中使用的千问模型为7B-Instruct,需要5g以…...

守护生产车间安全:可燃气体报警器预警与检测的重要性
近日,东莞一材料厂发生的火灾事故再次敲响了工业安全生产的警钟。 这起事故不仅给工厂带来了巨大的经济损失,也暴露了一些企业在安全管理方面的疏漏。其中,可燃气体报警器的应用与预警功能在火灾防范中扮演了至关重要的角色。 接下来&#…...

[创业之路-125] :制造业企业的必备管理神器-ERP-计算的资源管理与企业的资源管理的异同
目录 一、计算机的资源与企业资源相同点与不同点 1.1 相同点: 1.2 不同点: 二、计算机的内存管理与企业的库存管理相同点与不同点 2.1 相同点: 2.2 不同点: 一、计算机的资源与企业资源相同点与不同点 计算机的资源与企业资…...

TDengine Cloud 新增签约,这次是能源物联网平台
最近,全托管的物联网、工业大数据云服务平台 TDengine Cloud 新增一项签约🥳。为进一步提升平台的数据处理能力与系统稳定性,推动智能设备数据管理和能效优化到新的高度, 德中恒越物联网数据平台选择应用 TDengine Cloud ☁️。 …...

Kafka 最佳实践:构建高性能、可靠的数据管道
目录 1. 部署最佳实践 1.1 硬件配置 1.2 集群配置 1.3 ZooKeeper 配置 2. 主题和分区设计 2.1 分区设计 2.2 数据保留策略 3. 生产者最佳实践 3.1 生产确认机制 3.2 重试机制 3.3 批量发送 4. 消费者最佳实践 4.1 消费组管理 4.2 并行处理 4.3 错误处理 5. 安全…...

进军韩国5G市场!移远通信5G模组RG500L-EU率先获得KT、LGU+认证
近日,移远通信工规级5G模组RG500L-EU再传喜讯,率先通过了韩国两大运营商KT和LGU的严格认证。在此之前,该模组已顺利通过KC认证(韩国法规认证),此次再获运营商认证表明,RG500L-EU已完全满足韩国…...

http/2 二进制分帧层 (Binary Framing Layer)讲解
文章目录 二进制帧HTTP/2 中的帧、消息和流1. 帧(Frame)2. 消息(Message)3. 流(Stream)总结示例: 二进制帧结构1.帧头部结构2.帧负载数据 请求和响应多路复用 链接参考:https://web.…...