Java基础关键_018_集合(二)

目 录

[一、泛型 ※](#一、泛型 ※)

1.说明

2.实例

3.擦除与补偿

4.泛型的定义

(1)类定义

(2)静态方法定义

(3)接口定义

5.通配符

(1)无限定

(2)上限

(3)下限

二、集合迭代的并发修改异常

1.示例

2.说明

[3. fail-fast 机制实现原理](#3. fail-fast 机制实现原理)

[三、List 接口](#三、List 接口)

1.特点

2.常见实现类

3.特有方法

[(1)add(int index, E element)](#(1)add(int index, E element))

[(2) set(int index, E element)](#(2) set(int index, E element))

[(3) get(int index)](#(3) get(int index))

[(4)remove(int index)](#(4)remove(int index))

[(5)indexOf(Object o)](#(5)indexOf(Object o))

[(6)lastIndexOf(Object o)](#(6)lastIndexOf(Object o))

[(7)subList(int fromIndex, int toIndex)](#(7)subList(int fromIndex, int toIndex))

[(8)of(E... elements)](#(8)of(E... elements))

4.特有迭代

(1)listIterator()

[(2) listIterator(int index)](#(2) listIterator(int index))

[四、ListIterator 的方法](#四、ListIterator 的方法)

1.hasNext()

2.next()

3.remove()

[4.add(E e)](#4.add(E e))

5.hasPrevious()

6.previous()

7.nextIndex()

8.previousIndex()

[9.set(E e)](#9.set(E e))

五、比较

[1.回顾数组 Comparable 比较](#1.回顾数组 Comparable 比较)

[2.集合 Comparator 比较](#2.集合 Comparator 比较)


一、泛型 ※

1.说明

  1. jdk 5 新增,属于编译阶段功能;
  2. 不使用泛型,会导致代码冗长,从集合中抽取的元素若要访问子类中特有的方法,需要向下转型;
  3. 泛型可以在开发时指定集合中存储的数据类型;
  4. 使用泛型除了可以简化代码,避免类型转换操作之外,还可以在编译时进行类型检查,避免了在运行时出现类型错误的问题;
  5. jdk 7 之后,可以省略表达式后面的泛型类型。

2.实例

java 复制代码
public class GenericTest {
    public static void main(String[] args) {
//        Collection<Person> c = new ArrayList<Person>();   //  jdk 1.7之前,必须写后面的泛型类型
        Collection<Person> c = new ArrayList<>();   //  jdk 1.7之后,可以省略后面的泛型类型
        // Collection<Person> c = new ArrayList<Person>();
        Person p1 = new Person("张三");
        Person p2 = new Person("李四");
        Person p3 = new Person("王五");
        c.add(p1);
        c.add(p2);
        c.add(p3);
//        c.add("赵六");    // 报错,类型不匹配
        Iterator<Person> iterator = c.iterator();
        while (iterator.hasNext()) {
            Person p = iterator.next();
            p.eat();
        }
        /*
         * 张三正在吃饭!
         * 李四正在吃饭!
         * 王五正在吃饭!
         * */
    }
}

3.擦除与补偿

  1. 泛型的出现提高了编译时的安全性,在编译时对数据类型进行检查,属于编译时期的技术。加载类时,会将泛型擦除,擦除后类型为 Object 类型;

  2. 泛型擦除是为了让 jdk 5 与之前版本的 jdk 能够兼容同一个类加载器;

  3. 因为泛型擦除后,变为 Object 类型。为了保证获得集合元素类型为实际类型,虚拟机会根据元素实际类型自动进行向下转型,这一过程称为泛型补偿。


4.泛型的定义

(1)类定义

java 复制代码
public class GenericTest<T> {
    private T name;

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }

    public GenericTest(T name) {
        this.name = name;
    }

    public static void main(String[] args) {
        GenericTest<String> gt = new GenericTest<>("张三");
        System.out.println(gt.getName());
//        GenericTest<String> gt1 = new GenericTest<>(123);   // 报错,类型不匹配,应该是String类型
    }
}

(2)静态方法定义

java 复制代码
public class GenericTest {
    public static <E> void test(E[] elts) {
        for (E elt : elts) {
            System.out.print(elt + "\t");
        }
        // 1	2	3	4	5
    }

    public static void main(String[] args) {
        Integer[] ints = {1, 2, 3, 4, 5};
        GenericTest.test(ints);
    }
}

(3)接口定义

java 复制代码
public interface GenericTest01<T> {
    String get(T t);
}
java 复制代码
public class GenericTest01Impl implements GenericTest01<GenericTest01Impl> {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public GenericTest01Impl(String name) {
        this.name = name;
    }

    @Override
    public String get(GenericTest01Impl gti) {
        return getName();
    }

    public static void main(String[] args) {
        GenericTest01Impl gt = new GenericTest01Impl("GenericTest01Impl");
        System.out.println(gt.get(gt));    // GenericTest01Impl
    }
}

5.通配符

(1)无限定

【 <?> 】,其中 【 ? 】 可以是任意引用数据类型。

java 复制代码
public class GenericTest {
    public static void get(ArrayList<?> list) {
    }

    public static void main(String[] args) {
        get(new ArrayList<String>());
        get(new ArrayList<Integer>());
        get(new ArrayList<Double>());
        get(new ArrayList<Object>());
    }
}

(2)上限

【 <? extends T> 】,其中 【 ? 】 必须是 T 及其子类。

java 复制代码
public class GenericTest {
    public static void get(ArrayList<? extends Number> list) {
    }

    public static void main(String[] args) {
//        get(new ArrayList<String>());   // 报错, 不能将类型为java.util.ArrayList<java.lang.String>的表达式赋给类型为java.util.ArrayList<? extends java.lang.Number>的变量
        get(new ArrayList<Integer>());
        get(new ArrayList<Double>());
//        get(new ArrayList<Object>());   // 报错, 不能将类型为java.util.ArrayList<java.lang.Object>的表达式赋给类型为java.util.ArrayList<? extends java.lang.Number>的变量
    }
}

(3)下限

【 <? super T> 】,其中 【 ? 】 必须是 T 及其父类。

java 复制代码
public class GenericTest {
    public static void get(ArrayList<? super Number> list) {
    }

    public static void main(String[] args) {
//        get(new ArrayList<String>());  // 报错,不允许
//        get(new ArrayList<Double>());   // 报错,不允许
        get(new ArrayList<Object>());
        get(new ArrayList<Number>());
    }
}

二、集合迭代的并发修改异常

1.示例

在进行集合迭代需要三个步骤,可以参考 Java基础关键_017_集合(一) ,但是在删除集合中的元素时,会抛出并发修改异常,示例如下:

java 复制代码
public class GenericTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if (name.equals("李四")) {
                list.remove(name);  // 调用集合remove方法删除元素会报错 java.util.ConcurrentModificationException(并发修改异常)
            }
            System.out.println(name);
        }
    }
}

面对以上异常,应该怎么做呢?

应该将集合的 remove 方法替换为迭代器的 remove 方法,在第二次迭代时就会发现"李四"被删除了,示例如下:

java 复制代码
public class GenericTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if (name.equals("李四")) {
                iterator.remove();  // 调用迭代器的remove方法删除当前元素
            }
        }
        Iterator<String> iterator1 = list.iterator();
        while (iterator1.hasNext()) {
            String name = iterator1.next();
            System.out.println(name);
        }
    }
}

2.说明

因为使用迭代器遍历,而使用集合删除操作去删除,底层为防止多线程导致的错误发生,所以抛出并发修改异常,这一机制称为快速失败,即 fail-fast 。


3. fail-fast 机制实现原理

  1. 集合中设置有 modCount 属性,记录修改次数使用集合对象进行 增、删、改 操作时,modCount 会加 1 ;

  2. 获取迭代器对象时,会初始化一个 expectedModCount 属性,并将其值初始化为 modCount ;

  3. 当使用集合的 remove 方法删除元素时,会导致 modCount 加 1 ,而 expectedModCount 不变;

  4. 当 modCount 和 expectedModCount 值不相等时,底层代码会抛出并发修改异常。


三、List 接口

1.特点

有序、可重复。


2.常见实现类

  1. ArrayList:数组;
  2. Vector、Stack:线程安全的数组;
  3. LinkedList:双向链表。

3.特有方法

只适合在 List 中使用,都与下标有关。

(1)add(int index, E element)

在指定索引处插入元素。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add(1, "赵六");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + "\t");
        }
        // 张三	赵六	李四	王五	
    }
}

(2) set(int index, E element)

修改索引处的元素。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.set(0, "赵六");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + "\t");
        }
        // 赵六	李四	王五
    }
}

(3) get(int index)

根据索引获取元素。List 集合独特的遍历方式:通过下标遍历。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        System.out.println(list.get(1));    // 李四
    }
}

(4)remove(int index)

删除索引处的元素。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.remove(1);
        for (String s : list) {
            System.out.println(s);
        }
        // 张三
        // 王五
    }
}

(5)indexOf(Object o)

获取对象 o 在集合中第一次出现的索引。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("张三");
        list.add("王五");
        System.out.println(list.indexOf("张三")); // 0
    }
}

(6)lastIndexOf(Object o)

获取对象 o 在集合中最后一次出现的索引。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("张三");
        list.add("王五");
        System.out.println(list.lastIndexOf("张三")); // 2
    }
}

(7)subList(int fromIndex, int toIndex)

截取子 List 集合,生成一个新的集合,范围是 [ fromIndex, toIndex )。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("张三");
        list.add("王五");
        System.out.println(list.subList(1, 3)); // [李四, 张三]
    }
}

(8)of(E... elements)

静态方法,返回包含任意数量元素的不可修改列表,所获取的集合是只读的。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> strings = List.of("H", "e", "l", "l", "o");
        strings.set(1, "Q");    // 报错,java.lang.UnsupportedOperationException
    }
}

4.特有迭代

(1)listIterator()

获取 List 集合特有的迭代器。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        /*
         * 通用迭代器
         * */
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.print("Iterator:" + next + "\t");
        }
        // Iterator:张三	Iterator:李四	Iterator:王五	Iterator:赵六
        System.out.println();

        /*
         * 特有迭代器
         * */
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            String next = listIterator.next();
            System.out.print("ListIterator:" + next + "\t");
        }
        // ListIterator:张三	ListIterator:李四	ListIterator:王五	ListIterator:赵六
    }
}

(2) listIterator(int index)

从列表指定位置开始,返回列表中元素的列表迭代器。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        /*
         * 特有迭代器
         * */
        ListIterator<String> listIterator = list.listIterator(2);
        while (listIterator.hasNext()) {
            String next = listIterator.next();
            System.out.print(next + "\t");
        }
        // 王五	赵六
    }
}

四、ListIterator 的方法

1.hasNext()

通用方法,判断当前指向位置是否有元素。


2.next()

通用方法,将当前指向元素返回,并指向下一位。


3.remove()

通用方法,删除上一次 next() 方法返回的数据。必须先调用 next() 方法或 previous() 方法,否则会报错。


4.add(E e)

特有方法,将元素添加到指向位置,然后指向下一位。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            if ("王五".equals(listIterator.next())){
                listIterator.add("冯七");
            }
        }
        System.out.println(list);   // [张三, 李四, 王五, 冯七, 赵六]
    }
}

5.hasPrevious()

特有方法,判断当前指向位置的上一个位置是否存在元素。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        ListIterator<String> listIterator = list.listIterator();
        System.out.println(listIterator.hasPrevious()); // false
        while (listIterator.hasNext()) {
            listIterator.next();
        }
        System.out.println(listIterator.hasPrevious()); // true
    }
}

6.previous()

特有方法,获取上一个元素,即指向上一个元素并返回。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            listIterator.next();
        }
        System.out.println(listIterator.previous()); // 赵六
        System.out.println(listIterator.previous()); // 王五
    }
}

7.nextIndex()

特有方法,获取指向位置的下标。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            listIterator.next();
        }
        System.out.println(listIterator.nextIndex()); // 4
        System.out.println(listIterator.previous()); // 赵六
        System.out.println(listIterator.nextIndex()); // 3

    }
}

8.previousIndex()

特有方法,获取指向位置的上一个位置的下标。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            listIterator.next();
        }
        System.out.println(listIterator.previousIndex()); // 3
        System.out.println(listIterator.previous()); // 赵六
        System.out.println(listIterator.previousIndex()); // 2

    }
}

9.set(E e)

特有方法,修改上一次 next() 方法返回的数据。必须先调用 next() 方法或 previous() 方法,否则会报错。

java 复制代码
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            if ("王五".equals(listIterator.next())) {
                listIterator.set("冯七");
            }
        }
        System.out.println(list);   // [张三, 李四, 冯七, 赵六]
    }
}

五、比较

1.回顾数组 Comparable 比较

java 复制代码
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
//        return this.age - o.age;    // 比较规则, 年龄升序

//        return o.age - this.age;    // 比较规则, 年龄降序

//        return this.name.compareTo(o.name);    // 比较规则,姓名升序

        return o.name.compareTo(this.name);    // 比较规则,姓名降序
    }
}
java 复制代码
public class ComparableTest {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 18);
        Student s2 = new Student("李四", 22);
        Student s3 = new Student("王五", 7);
        Student[] students = {s1, s2, s3};
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

2.集合 Comparator 比较

java 复制代码
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
java 复制代码
public class StudentComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
         return o1.getAge() - o2.getAge();   // 排序规则,年龄升序

//        return o2.getAge() - o1.getAge();   // 排序规则,年龄降序

//        return o1.getName().compareTo(o2.getName());    // 排序规则,姓名升序

//        return o2.getName().compareTo(o1.getName());    // 排序规则,姓名降序
    }
}
java 复制代码
public class ComparatorTest {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("张三", 18));
        students.add(new Student("李四", 22));
        students.add(new Student("王五", 7));
        students.sort(new StudentComparator());
        ListIterator<Student> iterator = students.listIterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}
相关推荐
FreemanGordon26 分钟前
Java volatile 关键字
java
北京_宏哥27 分钟前
《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
java·前端·selenium
北京_宏哥35 分钟前
《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
java·selenium·前端工程化
当归10241 小时前
微服务与消息队列RabbitMQ
java·微服务
Lx3521 小时前
《从头开始学java,一天一个知识点》之:循环结构:for与while循环的使用场景
java·后端
Cache技术分享1 小时前
15. Java 如何声明一个变量来引用数组
java·前端
雷渊1 小时前
深入分析理解mysql的MVCC
java·数据库·面试
知其然亦知其所以然1 小时前
Java 高级面试题:Lock 到底比 synchronized 强在哪?
java·后端·面试
风象南1 小时前
Spring Boot 的 20个实用技巧
java·spring boot
Java陈序员1 小时前
IDEA 必备插件!轻松搞定 JSON 格式化!
java·json·intellij idea