课前回顾:1.进程:进入到内存执行的应用程序2.线程:应用程序中的一个执行单元,CPU和内存之间的通道3.并发:在同一个时刻,多条线程同时操作同一个资源4.线程的创建:a.继承Thread 重写run方法,设置线程任务b.实现Runnable接口 重写run方法 设置线程任务c.匿名内部类: new Thread(new Runnable(){重写run})5.Thread类中的方法a.getName()->获取线程名字b.Thread currentThread()->获取当前正在执行的线程对象c.setName()->给线程设置名字d.sleep(long time)->线程睡眠->不会释放锁,超时之后继续执行e.start()->开启线程,jvm自动执行run方法6.解决线程安全我问题a.同步代码块:synchronized(任意对象)b.同步方法:普通的:修饰符 synchronized 返回值类型 方法名(参数){}默认锁:this静态的:修饰符 static synchronized 返回值类型 方法名(参数){}默认锁:类名.classc.注意:想要实现线程同步,多个线程使用的锁对象必须是同一个对象7.死锁:锁嵌套8.线程状态:新建 可运行 锁阻塞 无限等待 计时等待 被终止9.wait和notifywait:线程等待,会释放锁,需要其他的线程notify唤醒,唤醒之后重新抢锁,抢到了继续执行,抢不到,锁阻塞wait和notify需要锁对象调用,必须是同一把锁今日重点:1.会简单使用lock锁2.会简单使用线程池3.会使用Callable接口实现多线程4.会使用Collection集合中的方法5.会使用迭代器遍历集合6.会使用ArrayList集合
第一章.多等待多唤醒
一.解决多生产多消费问题(if改为while,将notify改为notifyAll)
public class BaoZiPu {//定义count,表示包子private int count;//定义flag,证明有么有包子private boolean flag;public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}//消费线程要调用的方法,证明消费包子public synchronized void getCount() {while (flag == false) {//证明没有包子,消费线程waittry {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//出了if就要消费包子System.out.println("消费了第..." + count + "个包子");//改变flag状态,为false,证明消费完毕,没有包子了flag = false;//唤醒生产线程this.notifyAll();}//生产者要调用的方法,证明生产包子public synchronized void setCount() {while (flag == true) {//证明有包子,生产线程waittry {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//出了if就要生产包子count++;System.out.println("生产了第........." + count + "个包子");//改变flag状态,为true,证明生产完毕,有包子了flag = true;//唤醒消费线程this.notifyAll();}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}}
//消费者public class Consumer implements Runnable{private BaoZiPu baoZiPu;public Consumer(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {e.printStackTrace();}//消费baoZiPu.getCount();}}}
//生产者public class Product implements Runnable{private BaoZiPu baoZiPu;public Product(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {e.printStackTrace();}//生产baoZiPu.setCount();}}}
public class Test {public static void main(String[] args) {BaoZiPu baoZiPu = new BaoZiPu();Product product = new Product(baoZiPu);Consumer consumer = new Consumer(baoZiPu);new Thread(product).start();new Thread(product).start();new Thread(consumer).start();new Thread(consumer).start();}}
第二章.Lock锁
1.Lock对象的介绍和基本使用
1.概述:锁对象 接口2.使用:通过Lock的实现类 -> ReentrantLock3.方法:void lock() -> 获取锁void unlock() ->释放锁
public class Ticket implements Runnable {int ticket = 100;//创建Lock对象Lock lock = new ReentrantLock();@Overridepublic void run() {while (true) {//获取锁lock.lock();if (ticket > 0) {try {Thread.sleep(100L);//买票System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");ticket--;} catch (InterruptedException e) {e.printStackTrace();}finally {//释放锁lock.unlock();}}}}}
public class Test {public static void main(String[] args) {Ticket ticket = new Ticket();Thread t1 = new Thread(ticket, "无忌");Thread t2 = new Thread(ticket, "三丰");Thread t3 = new Thread(ticket, "翠山");//开启三个线程t1.start();t2.start();t3.start();}}
2.Lock和synchronized区别
2.1.Lock
1.Lock锁属于一种轻量级锁(乐观锁)2.Lock底层实现利用了CAS机制(乐观锁)3.乐观锁实现原理:CAS -> 代表的是 compare and swap(比较并交换)
![day14[多线程_集合] - 图2](/uploads/projects/liuye-6lcqc@vk53cd/34c3ac3971408990357bacd73d9127b0.png)
2.2.synchronized
属于悲观锁,一个线程在操作数据时,其他线程不能操作
2.3.Lock和synchronized区别(悲观锁和乐观锁的区别)
a.Lock属于乐观锁,使用多线程操作的是同一个变量synchronized属于悲观锁,使用多线程操作一段代码b.乐观锁:线程A在操作变量的时候,允许线程B操作,只是会判断,如果有问题,就放弃本次操作。判断如 果没有问题,则会正常执行本次操作悲观锁:当线程A正在操作的时候,不允许B线程执行,要等A出来之后B才有可能进去。c.相对来说悲观锁效率更低,乐观锁效率高。
多个线程操作同一个数据时:很容易出现3个问题
可见性( 解决用一个关键字 volatile解决)
重排( 解决用一个关键字 volatile解决)
原子性(volatile解决不了原子性的问题,所以为了解决多线程操作同一个数据出现的原子性问题,我们可以使用原子类Atomicxxx类)
第三章.Condition(阻塞队列)
1.Condition:阻塞队列2.获取:利用Lock对象中的方法:Condition newCondition()3.方法:void await() -> 线程等待void signal()->线程唤醒4.注意:Condition必须和Lock锁结合使用
public class BaoZiPu {//定义count,表示包子private int count;//定义flag,证明有么有包子private boolean flag;Lock lock = new ReentrantLock();//创建生产者的阻塞队列Condition productCondition = lock.newCondition();//创建消费者的阻塞队列Condition consumerCondition = lock.newCondition();public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}//消费线程要调用的方法,证明消费包子public void getCount() {//获取锁lock.lock();while (flag == false) {//证明没有包子,消费线程waittry {consumerCondition.await();} catch (InterruptedException e) {e.printStackTrace();}}//出了if就要消费包子System.out.println("消费了第..." + count + "个包子");//改变flag状态,为false,证明消费完毕,没有包子了flag = false;//唤醒生产线程productCondition.signal();//释放锁lock.unlock();}//生产者要调用的方法,证明生产包子public void setCount() {//获取锁lock.lock();while (flag == true) {//证明有包子,生产线程waittry {productCondition.await();} catch (InterruptedException e) {e.printStackTrace();}}//出了if就要生产包子count++;System.out.println("生产了第........." + count + "个包子");//改变flag状态,为true,证明生产完毕,有包子了flag = true;//唤醒消费线程consumerCondition.signal();//释放锁lock.unlock();}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}}
//生产者public class Product implements Runnable{private BaoZiPu baoZiPu;public Product(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {e.printStackTrace();}//生产baoZiPu.setCount();}}}
//消费者public class Consumer implements Runnable{private BaoZiPu baoZiPu;public Consumer(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {e.printStackTrace();}//消费baoZiPu.getCount();}}}
public class Test {public static void main(String[] args) {BaoZiPu baoZiPu = new BaoZiPu();Product product = new Product(baoZiPu);Consumer consumer = new Consumer(baoZiPu);new Thread(product).start();new Thread(product).start();new Thread(consumer).start();new Thread(consumer).start();}}
第四章.线程池
1.原始线程实现方案:频繁的创建线程,频繁的销毁线程,很消耗资源
![day14[多线程_集合] - 图3](/uploads/projects/liuye-6lcqc@vk53cd/d4679d30dee905d63452e26464722d75.png)
1.表示线程池的对象:Executors2.创建线程池,指明线程池中最多有多少个线程Executors中有一个静态方法:static ExecutorService newFixedThreadPool(int nThreads)nThreads:代表的是线程池中最多能有多少条线程对象ExecutorService:用于管理线程池中的线程对象3.ExecutorService类中的方法:Future<?> submit(Runnable task)->提交线程任务void shutdown() -> 关闭线程池4.Future接口:用于接收Runnable中run方法的返回值run方法没有返回值的,所以我们调用submit的时候,不用Future去接收
public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"执行了");}}
public class Test {public static void main(String[] args) {//创建线程池对象ExecutorService es = Executors.newFixedThreadPool(2);//调用ExecutorService中的submit方法,提交线程任务es.submit(new MyRunnable());es.submit(new MyRunnable());es.submit(new MyRunnable());//关闭线程池//es.shutdown();}}
第五章.Callable接口
<T> Future<T> submit(Callable<T> task)1.概述:Callable接口,用于实现多线程2.Callable中有一个方法:v call()->设置线程任务call的返回值类型取决于Callable<类型>3.call方法和run方法有什么区别:相同点:都是设置线程任务不同点:run() 没有返回值,不能直接throws异常call() 有返回值,能throws异常4.提交线程任务:<T> Future<T> submit(Callable<T> task)5.Future接口:接收run方法的返回值或者call方法的返回值Future接口中的方法:V get()->获取的是call方法返回的数据
public class Test01 {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService es = Executors.newFixedThreadPool(2);Future<String> future = es.submit(new MyCallable());System.out.println(future.get());}}
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "hia hia hia";}}
1.创建线程池:Executors类中的方法newFixedThreadPool(2)确定线程池中最多有几个线程,返回ExecutorService
2.使用ExecutorService中的submit方法,提交线程任务
submit(Runnable),无需用Future接受返回值,因为Runnable中的run没有返回值
submit(Callable) ,需要用Future接收返回值,因为Callable中的call方法是有返回值的
3.如何获取到call的返回值
用Future中的get方法获取
第六章.Timer定时器
1.作用:让一个线程任务,每隔一段时间就执行一次2.构造:Timer()3.方法:void schedule(TimerTask task, Date firstTime, long period)task:设置线程任务firstTime:从什么时间开始计算period:隔多长时间执行一次线程任务
public class Demo01Timer {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("柳岩对涛哥说:涛哥快起床了!");}},new Date(),2000L);}}
第七章.集合框架(单列集合)
1.集合分类:1.单列集合:Collection接口为首存储元素的: list.add("abc")2.双列集合:Map接口为首map.put("涛哥","柳岩")2.概述:容器3.作用:存储数据4.特点:1.长度可变2.只能存储引用数据类型(即使存储了基本类型到了集合中也会自动转成对应的引用类型(包装类))
![day14[多线程_集合] - 图4](/uploads/projects/liuye-6lcqc@vk53cd/77585701fbe8cae7ae8cdc0900ff8d1b.png)
第八章.Collection接口
1.概述:单列集合的顶级接口2.使用:Collection<E> coll = new ArrayList<E>();3.<E>是啥意思:泛型,规定集合能存储什么类型的元素<Integer> -> 代表的是集合中存储的元素为整数<String> -> 代表的是集合中存储的元素为字符串如果不写泛型-> 元素默认类型Object-->不建议这么写4.方法:- public boolean add(E e): 把给定的对象添加到当前集合中 。add方法调用之后,我们一般不用返回值接收(add是一定能添加成功的)- public boolean addAll(Collection<? extends E>)将另一个集合元素添加到当前集合中。- public void clear() :清空集合中所有的元素。- public boolean remove(E e): 把给定的对象在当前集合中删除。- public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。- public boolean isEmpty(): 判断当前集合是否为空。- public int size(): 返回集合中元素的个数。- public Object[] toArray(): 把集合中的元素,存储到数组中。
public class Demo01Collection {public static void main(String[] args) {//创建集合Collection<String> collection = new ArrayList<String>();//- public boolean add(E e): 在集合尾部添加元素collection.add("张三");collection.add("李四");collection.add("王五");collection.add("赵六");collection.add("田七");collection.add("猪八");collection.add("猪八");System.out.println(collection);//- public boolean addAll(Collection<? extends E>)将另一个集合元素添加到当前集合中。->集合合并Collection<String> collection1 = new ArrayList<String>();collection1.add("林黛玉");collection1.add("贾宝玉");collection1.add("张曼玉");collection.addAll(collection1);System.out.println(collection);//- public void clear() :清空集合中所有的元素。/*collection.clear();System.out.println(collection);*///- public boolean remove(E e): 把给定的对象在当前集合中删除。boolean b = collection.remove("张三");System.out.println(b);System.out.println(collection);//- public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。boolean b1 = collection.contains("李四");System.out.println(b1);//- public boolean isEmpty(): 判断当前集合是否为空(集合中是否有元素)。//collection.clear();boolean empty = collection.isEmpty();System.out.println(empty);//- public int size(): 返回集合中元素的个数。System.out.println(collection.size());//- public Object[] toArray(): 把集合中的元素,存储到数组中。Object[] objects = collection.toArray();System.out.println(Arrays.toString(objects));}}
第九章.迭代器
1.迭代器基本使用
1.作用:将集合中的元素逐一地获取出来2.接口:Iterator3.获取:Collection接口中有一个方法:Iterator<E> iterator()4.Iterator中的方法:boolean hasNext()->判断集合中有没有下一个元素E next()-> 获取下一个元素
public class Test01 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("张三");list.add("李四");list.add("王五");list.add("赵六");//获取Iterator对象Iterator<String> iterator = list.iterator();while(iterator.hasNext()){String element = iterator.next();System.out.println(element);}}}
经验值:
切记,使用迭代器的时候不要连续使用next()方法
public class Test02 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("张三");list.add("李四");list.add("王五");//获取Iterator对象Iterator<String> iterator = list.iterator();while(iterator.hasNext()){String element = iterator.next();String element1 = iterator.next();System.out.println(element);System.out.println(element1);}}}
2.迭代器迭代原理
![day14[多线程_集合] - 图7](/uploads/projects/liuye-6lcqc@vk53cd/c982511a21ed966734c95ee706387319.png)
3.迭代器底层原理
1.问题: Iterator<String> iterator = list.iterator(); 获取迭代器,用Iterator接收,而Iterator是一个接口,获取之后肯定指向的是Iterator的实现类对象,Iterator指向的是哪个实现类?Iterator指向的是ArrayList的内部类(Itr)
![day14[多线程_集合] - 图8](/uploads/projects/liuye-6lcqc@vk53cd/6ce7fb8c427995b2676d6a28e4ba0a97.png)
注意:Iterator接口指向了Itr,仅仅局限于使用的是ArrayList集合
4.并发修改异常
需求:定义集合,存储 孙悟空 猪八戒 沙和尚 白龙马 唐僧遍历集合,如果遍历到了猪八戒,添加一个"涛哥"
public class Test03 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("孙悟空");list.add("猪八戒");list.add("唐僧");list.add("沙和尚");list.add("白龙马");Iterator<String> iterator = list.iterator();while (iterator.hasNext()){String element = iterator.next();if ("猪八戒".equals(element)){list.add("涛哥");}}System.out.println(list);}}
出现的异常:Exception in thread "main" java.util.ConcurrentModificationException->并发修改异常at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)at java.util.ArrayList$Itr.next(ArrayList.java:859)at com.atguigu.h_iterator.Test03.main(Test03.java:17)
String element = iterator.next();底层源码modCount:实际操作次数expectedModCount:预期操作次数public E next() {checkForComodification();}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}========================================================list.add("涛哥");底层源码:public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!}private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private void ensureExplicitCapacity(int minCapacity) {modCount++;}总结:出现并发修改异常的原因:当调用add时,底层给modCount++了但是modCount改变之后没有重新给expectedModCount赋值,导致了modCount和expectedModCount不相等了所以add后再调用next方法时.next方法底层做了一个判断当modCount和expectedModCount不相等时,抛出ConcurrentModificationException在使用迭代器的时候,不要随意修改集合长度
扩展:ListIterator接口
获取: ListIterator listIterator()
ListIterator接口中的方法:add(E e)
public class Test03 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("孙悟空");list.add("猪八戒");list.add("唐僧");list.add("沙和尚");list.add("白龙马");ListIterator<String> iterator = list.listIterator();while (iterator.hasNext()){String element = iterator.next();if ("猪八戒".equals(element)){iterator.add("涛哥");}}System.out.println(list);}}
第十章.数据结构
1.栈
1.有栈顶 有栈低2.先存进去的元素在栈低,后存进去会往上压3.特点:先进后出
![day14[多线程_集合] - 图9](/uploads/projects/liuye-6lcqc@vk53cd/476605c38a78066f2ac8952a3a99fc2a.png)
2.队列
特点:先进先出排队
![day14[多线程_集合] - 图10](/uploads/projects/liuye-6lcqc@vk53cd/532e068cf9967688d098608b7f29790c.png)
3.数组
1.特点:查询快,增删慢2.查询快:因为数组是有索引的,我们查询元素可以直接根据索引定位到这个元素上增删慢:由于数组定长,所以如果添加元素,删除元素,我们首先得创建一个新的数组,将老数组的元素复制到新数组中
4.链表
1.特点:查询慢,增删快2.分类:单向链表:不能保证元素有序双向链表:能保证元素有序
4.1单向链表
一个节点分两部分:数据域:存储的元素指针域:存储的是下一个节点的地址前面的节点记录后面节点的地址,但是后面节点的地址不记录前面节点的地址如果集合底层数据结构为单向链表,无法保证元素有序
![day14[多线程_集合] - 图11](/uploads/projects/liuye-6lcqc@vk53cd/fdebda8c8c3029cbf75c5a7f6ecd609f.png)
4.2双向链表
1.一个节点分三部分:第一个部分:记录上一个节点地址第二部分:数据域第三部分:记录下一个节点地址2.特点:查询慢,增删快前面的节点知道后面的节点后面的节点知道前面的节点
![day14[多线程_集合] - 图12](/uploads/projects/liuye-6lcqc@vk53cd/b1752c7f9224e754e750232a36f279df.png)
第十一章.List接口
1.概述:List extends Collection接口2.特点:有索引元素有序元素可重复
第十二章.List集合下的实现类
1.ArrayList集合
1.概述: 是List接口下的实现类2.特点:a.底层数据结构:数组b.元素有序c.有索引d.元素可重复3.使用:ArrayList<E> list = new ArrayList<E>()4.常用方法:- boolean add(E e) ->将元素添加到集合末尾(针对于ArrayList集合add方法一定能添加成功,所以我们不用返回值接收)- public void add(int index,E element):在指定索引位置上添加元素- public boolean remove(Object o):删除指定的元素,返回删除是否成功- public E remove(int index) :删除指定索引位置上的元素,返回的是被删除的元素- public E set(int index,E element):将指定索引位置上的元素,修改成后面的element- public E get(int index) :根据索引获取元素- public int size() :获取集合元素个数
1.1.ArrayList集合使用
public class Demo01ArrayList {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("张三");list.add("李四");list.add("王五");//- public void add(int index,E element):在指定索引位置上添加元素list.add(0,"二郎神");System.out.println(list);//- public boolean remove(Object o):删除指定的元素,返回删除是否成功/* boolean b = list.remove("张三");System.out.println(b);System.out.println(list);*///- public E remove(int index) :删除指定索引位置上的元素,返回的是被删除的元素/*String remove = list.remove(0);System.out.println(remove);System.out.println(list);*///- public E set(int index,E element):将指定索引位置上的元素,修改成后面的elementString s = list.set(0, "涛哥");System.out.println(s);System.out.println(list);//- public E get(int index) :根据索引获取元素String s1 = list.get(0);System.out.println(s1);//- public int size() :获取集合元素个数System.out.println(list.size());System.out.println("==============================");Iterator<String> iterator = list.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}System.out.println("====================");for (int i = 0;i<list.size();i++){System.out.println(list.get(i));}System.out.println("======================");for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}}}
集合名.fori
1.2.底层源码分析(扩展,扩展)
1.ArrayList底层数据结构:数组 查询快,增删慢2.特点:有序有索引元素可重复3.构造:new ArrayList(); //默认长度为10不是指的一new底层数组就是10,第一次添加(add)数据时,底层数组长度会变为10new ArrayList(int initialCapacity); //指定底层数组长度4.问题:假如创建出来的ArrayList集合底层数组长度为10,如果我们要添加第11个,那么底层长度为10的数组就要扩容了,扩容多少倍?-->1.5倍
结论:1.刚new出来的ArrayList集合,底层数组长度为0,我们第一次add的时候数组才会由0扩容为10原因是底层的elementData = Arrays.copyOf(elementData, newCapacity);2.为什么ArrayList底层是数组,但是我们说ArrayList长度可变?原因是底层的elementData = Arrays.copyOf(elementData, newCapacity);->底层实现数组复制3.如果使用有参构造创建ArrayList集合,底层直接就在有参构造中创建了指定长度的数组4.如果扩容的话:扩的是1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);新的容量 = 老的容量+(老的容量/2)
1.ArrayList中add方法底层源码分析:刚new出来的ArrayList集合,底层数组长度为0,我们第一次add的时候数组才会由0扩容为10ArrayList<String> list = new ArrayList<>();list.add("a");elementData->数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA->数组,默认为0public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}public boolean add(E e) {ensureCapacityInternal(size + 1); // 1}private void ensureCapacityInternal(int minCapacity) {minCapacity-->1ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//elementData数组 minCapacity->110}private static int calculateCapacity(Object[] elementData, int minCapacity) {//minCapacity 1if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);//minCapacity 1 DEFAULT_CAPACITY->10}return minCapacity;}ensureExplicitCapacity参数走完返回的是10,接下来开始走ensureExplicitCapacity方法private void ensureExplicitCapacity(int minCapacity) {//minCapacity->10if (minCapacity - elementData.length > 0)grow(minCapacity);//10}private void grow(int minCapacity) {//10int oldCapacity = elementData.length;//0int newCapacity = oldCapacity + (oldCapacity >> 1);//右移为除以2 左移为乘以2 newCapacity为0if (newCapacity - minCapacity < 0)newCapacity = minCapacity; //newCapacity 10if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);//newCapacity 10}elementData = Arrays.copyOf(elementData, newCapacity);//决定了ArrayList第一次add的时候底层数组长度由0扩容为了10这句话也解释了ArrayList底层为数组但是长度可变->数组复制==============================================================ArrayList的有参构造->可以指定底层数组长度ArrayList<String> list = new ArrayList<>(10);public ArrayList(int initialCapacity) {//10if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}
经验值:
public class Test01 {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(2);System.out.println(list);//调用删除removelist.remove(2);//错误写法System.out.println(list);}}
![day14[多线程_集合] - 图1](/uploads/projects/liuye-6lcqc@vk53cd/e2aedc141b408e897db61ce02034e2b2.png)
![day14[多线程_集合] - 图6](/uploads/projects/liuye-6lcqc@vk53cd/5dffa498e26c3dbba43452aac0f89463.png)
