课前回顾:
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 返回值类型 方法名(参数){}
默认锁:类名.class
c.注意:想要实现线程同步,多个线程使用的锁对象必须是同一个对象
7.死锁:锁嵌套
8.线程状态:
新建 可运行 锁阻塞 无限等待 计时等待 被终止
9.wait和notify
wait:线程等待,会释放锁,需要其他的线程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) {
//证明没有包子,消费线程wait
try {
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) {
//证明有包子,生产线程wait
try {
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;
}
@Override
public 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;
}
@Override
public 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的实现类 -> ReentrantLock
3.方法:
void lock() -> 获取锁
void unlock() ->释放锁
public class Ticket implements Runnable {
int ticket = 100;
//创建Lock对象
Lock lock = new ReentrantLock();
@Override
public 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(比较并交换)
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) {
//证明没有包子,消费线程wait
try {
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) {
//证明有包子,生产线程wait
try {
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;
}
@Override
public 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;
}
@Override
public 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.原始线程实现方案:频繁的创建线程,频繁的销毁线程,很消耗资源
1.表示线程池的对象:
Executors
2.创建线程池,指明线程池中最多有多少个线程
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{
@Override
public 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> {
@Override
public 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() {
@Override
public void run() {
System.out.println("柳岩对涛哥说:涛哥快起床了!");
}
},new Date(),2000L);
}
}
第七章.集合框架(单列集合)
1.集合分类:
1.单列集合:Collection接口为首
存储元素的: list.add("abc")
2.双列集合:Map接口为首
map.put("涛哥","柳岩")
2.概述:容器
3.作用:存储数据
4.特点:
1.长度可变
2.只能存储引用数据类型(即使存储了基本类型到了集合中也会自动转成对应的引用类型(包装类))
第八章.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.接口:
Iterator
3.获取:
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.迭代器迭代原理
3.迭代器底层原理
1.问题: Iterator<String> iterator = list.iterator(); 获取迭代器,用Iterator接收,而Iterator是一个接口,获取之后肯定指向的是Iterator的实现类对象,Iterator指向的是哪个实现类?
Iterator指向的是ArrayList的内部类(Itr)
注意: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.特点:
先进后出
2.队列
特点:先进先出
排队
3.数组
1.特点:
查询快,增删慢
2.查询快:因为数组是有索引的,我们查询元素可以直接根据索引定位到这个元素上
增删慢:由于数组定长,所以如果添加元素,删除元素,我们首先得创建一个新的数组,将老数组的元素复制到新数组中
4.链表
1.特点:
查询慢,增删快
2.分类:
单向链表:不能保证元素有序
双向链表:能保证元素有序
4.1单向链表
一个节点分两部分:
数据域:存储的元素
指针域:存储的是下一个节点的地址
前面的节点记录后面节点的地址,但是后面节点的地址不记录前面节点的地址
如果集合底层数据结构为单向链表,无法保证元素有序
4.2双向链表
1.一个节点分三部分:
第一个部分:记录上一个节点地址
第二部分:数据域
第三部分:记录下一个节点地址
2.特点:
查询慢,增删快
前面的节点知道后面的节点
后面的节点知道前面的节点
第十一章.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):将指定索引位置上的元素,修改成后面的element
String 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)数据时,底层数组长度会变为10
new 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扩容为10
ArrayList<String> list = new ArrayList<>();
list.add("a");
elementData->数组
DEFAULTCAPACITY_EMPTY_ELEMENTDATA->数组,默认为0
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 1
}
private void ensureCapacityInternal(int minCapacity) {minCapacity-->1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//elementData数组 minCapacity->1
10
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {//minCapacity 1
if (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->10
if (minCapacity - elementData.length > 0)
grow(minCapacity);//10
}
private void grow(int minCapacity) {//10
int oldCapacity = elementData.length;//0
int newCapacity = oldCapacity + (oldCapacity >> 1);//右移为除以2 左移为乘以2 newCapacity为0
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //newCapacity 10
if (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) {//10
if (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);
//调用删除remove
list.remove(2);//错误写法
System.out.println(list);
}
}