目 录
[一、泛型 ※](#一、泛型 ※)
[3. fail-fast 机制实现原理](#3. fail-fast 机制实现原理)
[三、List 接口](#三、List 接口)
[(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))
[(2) listIterator(int index)](#(2) listIterator(int index))
[四、ListIterator 的方法](#四、ListIterator 的方法)
[4.add(E e)](#4.add(E e))
[9.set(E e)](#9.set(E e))
[1.回顾数组 Comparable 比较](#1.回顾数组 Comparable 比较)
[2.集合 Comparator 比较](#2.集合 Comparator 比较)
一、泛型 ※
1.说明
- jdk 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.擦除与补偿
泛型的出现提高了编译时的安全性,在编译时对数据类型进行检查,属于编译时期的技术。加载类时,会将泛型擦除,擦除后类型为 Object 类型;
泛型擦除是为了让 jdk 5 与之前版本的 jdk 能够兼容同一个类加载器;
因为泛型擦除后,变为 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 机制实现原理
集合中设置有 modCount 属性,记录修改次数使用集合对象进行 增、删、改 操作时,modCount 会加 1 ;
获取迭代器对象时,会初始化一个 expectedModCount 属性,并将其值初始化为 modCount ;
当使用集合的 remove 方法删除元素时,会导致 modCount 加 1 ,而 expectedModCount 不变;
当 modCount 和 expectedModCount 值不相等时,底层代码会抛出并发修改异常。
三、List 接口
1.特点
有序、可重复。
2.常见实现类
- ArrayList:数组;
- Vector、Stack:线程安全的数组;
- 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());
}
}
}