正则,异常、Collection、List集合

一,正则表达式

1,概述

正则表达式就是由一些特定的字符组成,代表的是一个规则。

2,书写规则

public boolean matches(String regex) 判断字符串是否匹配正则表达式,匹配返回true,不匹配返回false。

正则表达式的书写的规则

符号 含义 举例
? 0次或1次 \d?
* 0次或多次 \d* (abc)*
+ 1次或多次 \d+ (abc)+
{} 具体次数 a{7} \d
(?i) 忽略后面字符的大小写 (?i)abc
a((?i)b)c 只忽略b的大小写 a((?i)b)c
符号 含义 举例
[] 里面的内容出现一次 [abc]
^ 取反 [^abc]
&& 交集,不能写单个的& [a-z&&m-p]
. 任意字符 \n 回车符号不匹配
\ 转义字符 \d
\d 0-9 \d+
\D 非0-9 \D+
\s 空白字符
\S 非空白字符 [^\s]
\w 单词字符 [a-zA-Z_0-9]
\W 非单词字符 [^\w]
() 分组 a(bc)+
| 写在方括号外面表示并集 ab|AB

3,应用案例

需求: 校验用户输入的电话,邮箱,时间是否合法

通过往字符串的matches()方法中传入一个正则表达式来返回一个布尔值判断符不符合要求。

这里大致的了解就行,知道方法的使用,用到正则的时候上网上搜索

// 1: 创建一个邮箱正则
        String yreg = "[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+";
        String y1 = "110@qq.com";
        System.out.println(y1.matches(yreg));

// 2: 创建一个时间正则
        String sreg = "([1-9]\\d{3}-)(([0]{0,1}[1-9]-)|([1][0-2]-))(([0-3]{0,1}[0-9]))";
        String t1 = "2023-3-03";
        System.out.println(t1.matches(sreg));

// 3: 创建一个中文正则
        String zreg = "[\u4E00-\u9FA5]+";
        String z1 = "你好";
        System.out.println(z1.matches(zreg));

4,用于查找信息

正则的另一个作用,就是爬取一个文本中想要的数据;

需求如下:

请把下面文本中的电话,邮箱,座机号码,热线都爬取出来。

电话:18512516758,18512508907
或者联系邮箱: boniu@itcast.cn
座机电话:01036517895,010-98951256
邮箱:bozai@itcast.cn,
邮箱2:dlei0009@163.com,
热线电话:400-618-9090 ,400-618-4000,
4006184000,4006189090
// 1.定义爬取规则(正则表达式)
        String regex = "(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15})|400-?\\d{3,8}-?\\d{3,8}";
// 编译正则
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(data);

        while (matcher.find()) {
            String group = matcher.group();
            System.out.println(group);
        }
    18699997777
    boniu@itcast.cn
    01036517895
    010-98951256
    bozai@itcast.cn
    dlei0009@163.com
    400-618-9090
    400-618-4000
    4006184000
    4006189090

5,用于搜索替换,分割内容

方法名 说明
public String replaceAll(String regex , String newStr) 按照正则表达式匹配的内容进行替换
public String[] split(String regex): 按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。
        String str = "abc5234d678efg345hij245klm5234no534pq23rst543uvw534xy4z";
//        需求去掉字符串中的数字,知道是符合正则的条件,都给替换为空,结果如下
        String s = str.replaceAll("[0-9]*", "");
        System.out.println(s);  // abcdefghijklmnopqrstuvwxyz
        String str = "abc2d3e4f5g6h5i3j5k6lmnop3qrstuvwxyz";
// 按照数字切分
	    String[] split = str.split("\\d");
        for (String s1 : split) {
            System.out.print(s1 +"\t");
        }
// abc	d	e	f	g	h	i	j	k	lmnop	qrstuvwxyz	

最后还需要了解正则中的贪婪匹配和非贪婪匹配;

需求: 我们只想要姓名

public class Regex2 {
    public static void main(String[] args) {
        String data ="欢迎张全蛋光临本系统! 他删库并跑路,欢迎李二狗子光临本系统!欢迎马六子光临本系统! " +
                "它浏览了很多好看的照片!欢迎夏统光临!他在六点钟购买了一台拖拉机!";

        String regex1 = "欢迎(.+)光临";   // .+是贪婪匹配,尽量多的匹配,从后往前,还个字符匹配
        String regex2 = "欢迎(.+?)光临";  // .+?是非贪婪匹配(懒模式) 尽量小的匹配,从前往后,还个字符匹配
        Pattern compile = Pattern.compile(regex2);
        Matcher matcher = compile.matcher(data);
        while (matcher.find()) {
//            这里一个小括号是一组
            String group = matcher.group(1);
            System.out.println(group);
        }

什么意思呢,贪婪也就是整个的去匹配,直接看结果,如果这里compile()中使用的是regex1,结果如下:

// 张全蛋光临本系统! 他删库并跑路,欢迎李二狗子光临本系统!欢迎马六子光临本系统! 它浏览了很多好看的照片!欢迎夏统

比较贪婪嘛,返回的是第一个光临开始到最后一个光临结束的字符串。

而如果是非贪婪匹配就是离自己最近的,使用regex2 结果如下:

张全蛋
李二狗子
马六子
夏统

最后还有一点,先看需求:有重复的数据的时候,怎么去重呢?在replaceAll中传如下正则,这里我不理解。但是老师讲到了,老师说是固定的,所以这里就先记着有这样的一个方法,等到用的时候能够想起来就行

//        有重复的数据的时候,怎么去重呢?
        String str = "我我我我爱爱爱爱java";
        String res = str.replaceAll("(.)\\1+","$1");
        System.out.println(res);   // 我爱java

二,异常

1,认识异常

通过前面的学习,会有这样的一个问题,调用一个方法时,经常一部小心就出异常了,然后在控制台打印一些异常信息。其实打印的这些异常信息,就叫做异常。

在这里插入图片描述

因为写代码时经常会出现问题,Java的设计者们早就为我们写好了很多个异常类,来描述不同场景下的问题。而有些类是有共性的所以就有了异常的继承体系

在这里插入图片描述

抛出异常(throws)

在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理。

方法 throws 异常1 ,异常2 ,异常3 ..{
       …
 }

捕获异常(try...catch)

直接捕获程序出现的异常。

try{
// 监视可能出现异常的代码!
}catch(异常类型1 变量){
// 处理异常
}catch(异常类型2 变量){
// 处理异常
}...

先来演示一个运行时异常产生

int[] arr = {11,22,33};
//5是一个不存在的索引,所以此时产生ArrayIndexOutOfBoundsExcpetion
System.out.println(arr[5]); 

对于程序中的异常我们通常有两种解决方案。

  • 第一种:使用throws在方法上声明,意思就是告诉下一个调用者,这里面可能有异常啊,你调用时注意一下。
// throws ParseException    
public static void main(String[] args) throws ParseException{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse("2028-11-11 10:24");
        System.out.println(d);
}
  • 第二种:使用try...catch语句块异常进行处理。
public static void main(String[] args) throws ParseException{
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date d = sdf.parse("2028-11-11 10:24");
            System.out.println(d);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

2,自定义异常

尽管java 的编写这个提供了很多的异常类,但是无法为这个世界上的全部问题都提供异常类,如果企业自己的某种问题,想通过异常来表示,那就需要自己来定义异常类了。通过一个需求,来自定义异常

需求:写一个saveAge(int age)方法,在方法中对参数age进行判断,如果age<0 或者 >=130就认为年龄不合法,如果年龄不合法,就给调用者抛出一个年龄非法异常。

Java的API中是没有年龄非常这个异常的,所以我们可以自定义一个异常类,用来表示年龄非法异常,然后再方法中抛出自定义异常即可。

// 1、必须让这个类继承自Exception,才能成为一个编译时异常类。
public class AgeIllegalException extends Exception{
    public AgeIllegalException() {
    }

    public AgeIllegalException(String message) {
        super(message);
    }
}
  • 写一个测试类,在测试类中定义一个saveAge(int age)方法,对age判断如果年龄不在0~130之间,就抛出一个AgeIllegalException异常对象给调用者。
public class ExceptionTest2 {
    public static void main(String[] args) {
        // 需求:保存一个合法的年龄
        try {
            saveAge2(225);
            System.out.println("saveAge2底层执行是成功的!");
        } catch (AgeIllegalException e) {
            e.printStackTrace();
            System.out.println("saveAge2底层执行是出现bug的!");
        }
    }

	//2、在方法中对age进行判断,不合法则抛出AgeIllegalException
    public static void saveAge(int age){
        if(age > 0 && age < 150){
            System.out.println("年龄被成功保存: " + age);
        }else {
            // 用一个异常对象封装这个问题
            // throw 抛出去这个异常对象
            throw new AgeIllegalRuntimeException("/age is illegal, your age is " + age);
        }
    }
}

这个特殊说明一下: 自定义异常可能是编译时异常,也可以是运行时异常

1.如果自定义异常类继承Excpetion,则是编译时异常。
	特点:方法中抛出的是编译时异常,必须在方法上使用throws声明,强制调用者处理。
	
2.如果自定义异常类继承RuntimeException,则运行时异常。
	特点:方法中抛出的是运行时异常,不需要在方法上用throws声明。

3,异常的处理

有如下的场景:A调用B,B调用C;C中有异常产生抛给B,B中有异常产生又抛给A;异常到了A这里就不建议再抛出了,因为最终抛出被JVM处理程序就会异常终止,并且给用户看异常信息,用户也看不懂,体验很不好。

此时比较好的做法就是:1.将异常捕获,将比较友好的信息显示给用户看;2.尝试重新执行,看是是否能修复这个问题。

  • 第一种处理方式是,在main方法中对异常进行try...catch捕获处理了,给出友好提示。(只看过程)
    public static void main(String[] args)  {
        try {
            test1();
        } catch (FileNotFoundException e) {
            System.out.println("您要找的文件不存在!!");
            e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。
        } catch (ParseException e) {
            System.out.println("您要解析的时间有问题了!");
            e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。
        }
    }

    public static void test1() throws FileNotFoundException, ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse("2028-11-11 10:24:11");
        System.out.println(d);
        test2();
    }

    public static void test2() throws FileNotFoundException {
        // 读取文件的。
        InputStream is = new FileInputStream("D:/meinv.png");
    }
  • 第二种处理方式是:在main方法中对异常进行捕获,并尝试修复
 public static void main(String[] args) {
        // 需求:调用一个方法,让用户输入一个合适的价格返回为止。
        // 尝试修复
        while (true) {
            try {
                System.out.println(getMoney());
                break;
            } catch (Exception e) {
                System.out.println("请您输入合法的数字!!");
            }
        }
    }

    public static double getMoney(){
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请您输入合适的价格:");
            double money = sc.nextDouble();
            if(money >= 0){
                return money;
            }else {
                System.out.println("您输入的价格是不合适的!");
            }
        }
    }

三,集合

1,集合的分类

集合就像是一个容器,用来存储我们想存储的数据。根据集合中元素存储的特点进行分类学习,如下图所示:一类是单列集合元素是一个一个的,另一类是双列集合元素是一对一对的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n5mbbsyb-1677773757087)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230302230825366.png)]

下面的笔记先记录Collection集合中的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGMrALD9-1677773757087)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230302230924953.png)]

通过这个图我们可以看到,有两个接口实现了Collection接口,分别为Set接口和List接口;既然Collection是他们两个的父接口,所以定义的抽象方法,肯定是,他们两个能够通用的。下面先对这两个实现接口主要的特点做一个简单的介绍。

3,Collection集合中常用的方法

List集合: 集合中的元素有序<指存入时的顺序和取出的顺序一致>,2,集合中的元素可以重复,3,集合中的元素可以通过索引获取

Set集合(相反):集合中的元素无序,元素不可重复,不可通过索引获取

所以作为他们的父接口的抽象方法,必然是兼顾二者的方法;如下:

方法名 说明
public boolean add(E e) 把给定的对象添加到当前集合中
public void clear() 清空集合中所有的元素
public boolean remove(E e) 把给定的对象在当前集合中删除
public boolean contains(Object obj) 判断当前集合中是否包含给定的对象
public boolean isEmpty() 判断当前集合是否为空
public int size() 返回集合中元素的个数。
public Object[] toArray() 把集合中的元素,存储到数组中
Collection<String> c = new ArrayList<>();
//1.public boolean add(E e): 添加元素到集合
c.add("java1");
c.add("java1");
c.add("java2");
c.add("java2");
c.add("java3");
System.out.println(c); //打印: [java1, java1, java2, java2, java3]

//2.public int size(): 获取集合的大小
System.out.println(c.size()); //5

//3.public boolean contains(Object obj): 判断集合中是否包含某个元素
System.out.println(c.contains("java1")); //true
System.out.println(c.contains("Java1")); //false

//4.pubilc boolean remove(E e): 删除某个元素,如果有多个重复元素只能删除第一个
System.out.println(c.remove("java1")); //true
System.out.println(c); //打印: [java1,java2, java2, java3]

//5.public void clear(): 清空集合的元素
c.clear(); 
System.out.println(c); //打印:[]

//6.public boolean isEmpty(): 判断集合是否为空 是空返回true 反之返回false
System.out.println(c.isEmpty()); //true

//7.public Object[] toArray(): 把集合转换为数组
Object[] array = c.toArray();
System.out.println(Arrays.toString(array)); //[java1,java2, java2, java3]

//8.如果想把集合转换为指定类型的数组,可以使用下面的代码
String[] array1 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]

//9.还可以把一个集合中的元素,添加到另一个集合中
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2); //把c2集合中的全部元素,添加到c1集合中去
System.out.println(c1); //[java1, java2, java3, java4]

即ArrayList、LinkedList、vector、HashSet、LinkedHashSet、TreeSet集合都可以调用下面的方法。

四,Collection的遍历

有这样的一个动物类;用于编号id,姓名name这两个属性,包括对应的toString ,setter,getter方法,有参和无参构造,主要代码如下:

class Animal{
    private String id;
    private String name;
    // toString ,setter,getter方法,有参和无参构造,,,,省略
}

有这样一个测试类,在主方法中创建Animal中的元素:通过下面的几个方法来学习遍历集合

public static void main(String[] args) {
        Collection<Animal> animals = new ArrayList<>();
        animals.add(new Animal("1001","薛定谔的猫"));
        animals.add(new Animal("1002","芝诺的乌龟"));
        animals.add(new Animal("1003","麦克斯韦妖"));
        animals.add(new Animal("1004","拉普拉斯兽"));
    }

可能会想到最原始的遍历,for循环,这样就错了,因为Collection是Set的父类,所以必须要做到兼容,就不可能做到通过索引的方式来获取,只能通过特殊的方法,也就是下面的迭代器。

1,迭代器遍历集合

通过刚才的结构图中我们可以看到,Collection是Iterator的子类,所以就拥有了父类的迭代器的功能,在遍历的时候就可以使用。

先介绍Collection接口中获取迭代器的方法:

方法名称 说明
Iterator iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素

再介绍迭代器中的两个主要的方法:

方法名称 说明
boolean hasNext() 询问当前位置是否有元素存在,存在返回true ,不存在返回false
E next() 获取当前位置的元素,并同时将迭代器对象指向下一个元素处。

那么我们就可以使用了,

        
// 通过集合对象调用Iterator方法来获取一个迭代器,然后通过迭代中的两个方法,来实现遍历
Iterator<Animal> iterator = animals.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

注意:在使用迭代器的时候,不要去增加或者删除元素,这样很容易造成异常。也不要在一次循环中,通过next方法获取两次对象,否则也可能出问题,因为next调用一次就会向下一个数据挪动。

比如:如果说是最后一个hasNext判断为true,能够进去,第一次使用next获取数据后,指针会向后移动,但是后面已经没有元素了,所以在下一次再去执行next的时候,就会发生异常。

2,增强for遍历

底层使用的还是迭代器

    public Iterator<E> iterator() {
        return new Itr();
    }

格式:

for (元素的数据类型 变量名 : 数组或者集合) {
         
}

增强for可以用来遍历集合或者数组。

增强for遍历集合,本质就是迭代器遍历集合的简化写法

for (Animal animal : animals) {
    System.out.println(animal);
}

3,forEach遍历集合

得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。

方法名称 说明
default void forEach(Consumer<? super T> action) 结合lambda遍历集合

这个参数是一个函数式接口: 使用lambda方式如下: 网上对其的具体分析:【Java 8 新特性】Java Consumer示例_功能型接口consumer_猫巳的博客-CSDN博客

animals.forEach(animal -> System.out.println(animal));

五,List集合

1,特点,特有的方法

List系列集合特点: 有序,可重复,有索引

ArrayList:有序,可重复,有索引。

LinkedList:有序,可重复,有索引。

List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。

方法名称 说明
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
E get(int index) 返回指定索引处的元素

2, List集合的遍历方式

List集合相比于前面的Collection多了一种可以通过索引遍历的方式,所以List集合遍历方式一共有四种:

  • 普通for循环(只因为List有索引)
  • 迭代器
  • 增强for
  • Lambda表达式
List<String> list = new ArrayList<>();
list.add("java");
list.add("PHP");
list.add("Python");

//1.普通for循环
for(int i = 0; i< list.size(); i++){
    //i = 0, 1, 2
    String e = list.get(i);
    System.out.println(e);
}

//2.增强for遍历
for(String s : list){
    System.out.println(s);
}

//3.迭代器遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String s = it.next();
    System.out.println(s);
}

//4.lambda表达式遍历
list.forEach(s->System.out.println(s));

3,ArrayList集合的底层原理

ArrayList集合底层是基于数组结构实现的,也就是说往集合容器中存储元素时,底层本质上是往数组中存储元素。 特点如下:

记住他的特点,可以根据具体的环境选择不同的集合以提高程序的效率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7TmPUXfh-1677773757088)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000123292.png)]

我们知道数组的长度是固定的,但是集合的长度是可变的,这是怎么做到的。原理如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rSSighO-1677773757088)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000104028.png)]

数组扩容,并不是在原数组上扩容(原数组是不可以扩容的),底层是创建一个新数组,然后把原数组中的元素全部复制到新数组中去。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ciV4fl2R-1677773757089)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000206054.png)]

4,LinkedList底层原理

LinkedList底层是链表结构,链表结构是由一个一个的节点组成,一个节点由数据值、下一个元素的地址组成。如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-abiCD33H-1677773757089)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000251450.png)]

现在要在B节点和D节点中间插入一个元素,只需要把B节点指向D节点的地址断掉,重新指向新的节点地址就可以了。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EH9v0EUF-1677773757090)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000317587.png)]

现在想要把D节点删除,只需要让C节点指向E节点的地址,然后把D节点指向E节点的地址断掉。此时D节点就会变成垃圾,会把垃圾回收器清理掉。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3TF3Vf9G-1677773757090)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000334347.png)]

上面的链表是单向链表,它的方向是从头节点指向尾节点的,只能从左往右查找元素,这样查询效率比较慢;还有一种链表叫做双向链表,不光可以从做往右找,还可以从右往左找。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9IgQaGLL-1677773757090)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230303000353674.png)]

LinkedList集合是基于双向链表实现的,所以相对于ArrayList新增了一些可以针对头尾进行操作的方法,如下图示所示:

方法名称 说明
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素

因为LinkedList底层是双向链表,所以下面的特点就很好理解了

特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。

应用场景: 队列,栈 实例代码如下:

//1.创建一个队列:先进先出、后进后出
LinkedList<String> queue = new LinkedList<>();
//入对列
queue.addLast("第1号人");
queue.addLast("第2号人");
queue.addLast("第3号人");
queue.addLast("第4号人");
System.out.println(queue);

//出队列
System.out.println(queue.removeFirst());	//第4号人
System.out.println(queue.removeFirst());	//第3号人
System.out.println(queue.removeFirst());	//第2号人
System.out.println(queue.removeFirst());	//第1号人
//1.创建一个栈对象
LinkedList<String> stack = new ArrayList<>();
//压栈(push) 等价于 addFirst()
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack); //[第4颗子弹, 第3颗子弹, 第2颗子弹,第1颗子弹]

//弹栈(pop) 等价于 removeFirst()
System.out.println(statck.pop()); //第4颗子弹
System.out.println(statck.pop()); //第3颗子弹
System.out.println(statck.pop()); //第2颗子弹
System.out.println(statck.pop()); //第1颗子弹

//弹栈完了,集合中就没有元素了
System.out.println(list); //[]

5,Vector

Vector底层和ArrayLlist底层一样也是一个对象数组,

protected Object[] elementData;

Vector是线程同步的,即线程安全的 操作方法带有 synchronized 关键字(先了解)

在开发中需要线程同步安全的时候,考虑使用 Vector

底层结构 版本 线程安全(同步)效率 扩容倍数
ArrayList 可变数组 jdk1.2 不安全,效率高 如果有参构造1.5倍 如果无参 1,第一次是10 2,第二次是1.5倍扩容
Vector 可变数组 1.0 安全,效率不高 如果无参默认是10,满后,就按照两倍扩容 如果指定大小,则每次直接按两倍扩
posted @ 2023-03-03 00:23  yfs1024  阅读(35)  评论(0)    收藏  举报