Day16
一、迭代器
深入迭代器-foreach的底层
for (String element : list) { System.out.println(element); }
底层:
//使用foreach循环遍历集合的底层实现: String element; for(Iterator it = list.iterator();it.hasNext();System.out.println(element)) element = (String) it.next();
注意:Iterator it = list.iterator();中Iterator是一个接口,集合类都必须实现Iterable接口,而在Iterable接口中定义了一个方法,该方法要求返回一个Iterator接口实现类的对象。所以ArrayList类中必定有一个iterator()方法,该方法实际上是重写的Iterable接口的方法,返回的是iterator实现类对象。
//ArrayList中重写的Iterable接口的方法,返回iterator实现类对象 @Override public Iterator<E> iterator() { return new Itr(); }
因此也必定有一个类去实现iterator接口:
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such // To track modifications to the list during iteration int expectedModCount = modCount; // Checks if the list has more elements @Override public boolean hasNext() { return cursor != size(); } // Returns the next element in the iteration @SuppressWarnings("unchecked") @Override public E next() { checkForComodification(); int i = cursor; if (i >= size()) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } // Removes the last element returned by this iterator @Override public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } // Checks if the list has been modified during iteration final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } // Other methods and fields of ArrayList... }
深入迭代器 – Iterator
场景:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); list.add("ddd"); list.add("eee"); list.remove("bbb"); Iterator<String> it = list.iterator(); while(it.hasNext()){ String element = it.next(); System.out.println(element); } }
底层
Iterator接口:
public interface Iterator<E> { //判断是否有可迭代的元素 boolean hasNext(); //返回下一个元素 E next(); //删除(默认方法) -- 报错 default void remove() { throw new UnsupportedOperationException("remove"); } }
抽象类,用于记录外部操作数(添加和删除,改和查不影响数据数量)
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { //外部操作数(记录集合添加、删除的次数) protected transient int modCount = 0;//6 }
ArrayList类(一部分):
public class ArrayList<E> extends AbstractList<E> implements List<E>{ //数据容器 - ["aaa","ccc","ddd","eee",null,null,null,null,null,null] transient Object[] elementData; //数据个数(指针) private int size;//4 //e - "eee" public boolean add(E e) { ensureCapacityInternal(size + 1); // 判断是否扩容 elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } //o - "bbb" public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { //线性查询(从头遍历到size的位置) for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { //删除元素(传入下标) fastRemove(index); return true; } } return false; } //index - 1 private void fastRemove(int index) { modCount++; int numMoved = size - index - 1;//计算移动次数 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work } public Iterator<E> iterator() { return new Itr(); } //Itr是成员内部类,因为Itr中使用到了外部类(ArrayList)的成员属性(modCount、size) private class Itr implements Iterator<E> { int cursor; // 游标 - 4 int lastRet = -1; // 当前元素的下标 - 3 int expectedModCount = modCount;//内部操作数 - 6 public boolean hasNext() { return cursor != size;//4 != 4 } @SuppressWarnings("unchecked") public E next() { checkForComodification();//判断外部操作数和内部操作数是否相同 int i = cursor;//i = 3 if (i >= size) throw new NoSuchElementException(); //elementData - ["aaa","ccc","ddd","eee",null,null,null,null,null,null] Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1;//cursor - 4 return (E) elementData[lastRet = i]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification();//判断外部操作数和内部操作数是否相同 try { //Itr依赖于ArrayList对象的remove()去删除元素 ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; //重新将外部操作数赋值给内部操作数,保证内外部操作数一致不会出现脏数据 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } } }
理解:
1.transient Object[] elementData是一个数组容器,最开始默认长度为10,size为指针,代表数据的个数。2.ensureCapacityInternal()方法和 ensureExplicitCapacity()方法的作用为判断是否需要扩容,如果需要则调用扩容相关方法进行扩容。除此之外,还会 将外部操作数modCount++,因此在ArrayList底层中添加数据的流程就是先判断并扩容,然后添加数据到一维对象数组中并调整指针,指向下一个位置。
3.fastRemove()方法首先将外部操作数modCount++,然后计算多少个元素需要移动,由于删除的逻辑为删除当前位置的元素,并把后面的元素提前,所以计算方法为size - index - 1,如果该结果大于0(说明有要删除的元素),则用System.arraycopy和删除最后一个元素的组合技进行删除。因此删除数据的流程逻辑就是遍历元素然后调用fastRemove()方法进行删除。
4.ArrayList中用来实现Iterator的内部类为Itr,它定义了游标,当前元素的下标以及内部操作数三个属性。游标和size指针类似,用来指向下一个位置。当前元素下标初始值赋为-1。内部操作数值赋为modCount,即和外部操作数相等。hasNext()方法逻辑为当前游标和size指针是否相同,如果不同则说明游标还没到达最大数量,即存在元素。next()方法逻辑为先检查内部操作数和外部操作数是否相同,如果相同则再用游标返回对应的数组中的值。
Iterator<String> it = list.iterator(); while(it.hasNext()){ String element = it.next(); if(element.equals("bbb")){ //list.remove(element);//modCount - 6 it.remove(); } }
5.如果在迭代器遍历中要删除元素的话,不能用list.remove()方法,只能用it.remove()。即不能直接用ArrayList类中定义的remove方法,因为调用它会使得外部操作数加一,在迭代器遍历时next()方法就会报错,所以应该用Itr类中定义的remove()方法,重新将外部操作数赋值给内部操作数,保证内外部操作数一致不会出现脏数据。
深入迭代器 – ListIterator
逻辑和Iterator相同,但在Itr类中新增了一个内部类ListItr,里面有新增的方法,如add,set,指定下标,hasprevious(),previous(),倒序遍历等。
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); list.add("ddd"); list.add("eee"); ListIterator<String> listIterator = list.listIterator(); while(listIterator.hasNext()){ String element = listIterator.next(); if(element.equals("bbb")){ listIterator.set("xyz"); // 替换当前元素为 "xyz" listIterator.add("xyz"); // 在当前元素后添加新的 "xyz" listIterator.previous(); // 回到刚添加的 "xyz" 位置 listIterator.remove(); // 删除替换后的 "xyz" 元素 } } /** ListIterator<String> listIterator = list.listIterator(1); while(listIterator.hasNext()){ String element = listIterator.next(); System.out.println(element); } */ /** ListIterator<String> listIterator = list.listIterator(list.size()); while(listIterator.hasPrevious()){ String element = listIterator.previous(); System.out.println(element); } */ for (String element : list) { System.out.println(element); } }
二、泛型在集合中的使用
使用泛型的好处
- 类型安全:在编译时检查类型错误,防止将错误类型的对象添加到列表中。
- 可读性和可维护性:代码更容易理解和维护,因为类型信息显式显示在代码中。
- 避免类型转换:无需显式类型转换,因为泛型提供了编译时类型检查。
/** * @param <E> 创建对象时规定类型 * * E - element - 元素 * T - Type - 类型 * * 注意:设置泛型可以有多个 * <K,V> -> * K - Key 键 * V - Value 值 * * <N,V> -> * N - name 名 * V - Value 值 */ public class MyArrayList<E> { public void add(E e){ } }
public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(100); list.add(100); list.add(100); System.out.println("-----------------------------"); MyArrayList<Integer> myArrayList = new MyArrayList<>(); myArrayList.add(100); myArrayList.add(100); myArrayList.add(100); }
泛型限定
假设继承关系为继承关系:Object.A.B
public static void main(String[] args) { } //?表示任意类型 public static ArrayList<?> method01(){ // ArrayList<Object> list = new ArrayList<>(); // ArrayList<A> list = new ArrayList<>(); ArrayList<B> list = new ArrayList<>(); return list; } //? extends A 表示A类型或者是A的子类类型 public static ArrayList<? extends A> method02(){ // ArrayList<A> list = new ArrayList<>(); ArrayList<B> list = new ArrayList<>(); return list; } //? super A 表示A类型或者是A的父类类型 public static ArrayList<? super A> method03(){ // ArrayList<A> list = new ArrayList<>(); ArrayList<Object> list = new ArrayList<>(); return list; }
泛型的使用
需求:设计一个管理系统的接口
分析:
学生管理系统(数据的增删改查) – 学生对象
用户管理系统(数据的增删改查) – 用户对象
图书管理系统(数据的增删改查) – 图书对象
//管理系统的结构 public interface ManagerSystem<T> { public int add(T t); public int remove(T t); }
public class Student { } //学生管理系统的实现类 public class StudentManagerSystem implements ManagerSystem<Student>{ @Override public int add(Student t) { // TODO Auto-generated method stub return 0; } @Override public int remove(Student t) { // TODO Auto-generated method stub return 0; } }
public class User { } public class UserManagerSystem implements ManagerSystem<User>{ @Override public int add(User t) { // TODO Auto-generated method stub return 0; } @Override public int remove(User t) { // TODO Auto-generated method stub return 0; } }
public class Book { } public class BookManagerSystem<T> implements ManagerSystem<T>{ @Override public int add(T t) { // TODO Auto-generated method stub return 0; } @Override public int remove(T t) { // TODO Auto-generated method stub return 0; } }
public static void main(String[] args) { StudentManagerSystem sms = new StudentManagerSystem(); sms.add(new Student()); System.out.println("----------------------"); UserManagerSystem ums = new UserManagerSystem(); ums.add(new User()); System.out.println("----------------------"); BookManagerSystem<Book> bms01 = new BookManagerSystem<>(); bms01.add(new Book()); System.out.println("----------------------"); BookManagerSystem<String> bms02 = new BookManagerSystem<>(); bms02.add("Java从入门到精通"); bms02.add("MySQL从删库到跑路"); }
三、LinkedList
LinkedList的使用
1.ArrayList的方法都能用,但还有新的独有的方法:
public static void main(String[] args) { LinkedList<String> list = new LinkedList<>(); list.add("希1"); list.add("希2"); list.add("希3"); list.add("abc"); list.add("abc"); list.add("AAA"); list.add("abc"); list.add("abc"); //添加到头部 list.addFirst("aaa"); list.offerFirst("bbb"); list.push("ccc"); //添加到末尾 list.addLast("xxx"); list.offer("yyy"); list.offerLast("zzz"); System.out.println("获取第一个元素:" + list.element()); System.out.println("获取第一个元素:" + list.getFirst()); System.out.println("获取第一个元素:" + list.peek()); System.out.println("获取第一个元素:" + list.peekFirst()); System.out.println("获取第一个元素:" + list.pop()); System.out.println("获取最后一个元素:" + list.getLast()); System.out.println("获取最后一个元素:" + list.peekLast()); //删除第一个元素 list.poll(); list.pollFirst(); list.removeFirst(); //删除最后一个元素 list.pollLast(); list.removeLast(); //删除第一次出现的元素 list.removeFirstOccurrence("abc"); //删除最后一次出现的元素 list.removeLastOccurrence("abc"); //倒序遍历 // Iterator<String> descendingIterator = list.descendingIterator(); // while(descendingIterator.hasNext()){ // String next = descendingIterator.next(); // System.out.println(next); // } for (String element : list) { System.out.println(element); } }
注意这里的descendingIterator迭代不是用hasPrevious()和previoues(),因为底层代码用的是hasPrevious()和previoues()来编写的,所以这里直接用next即可。
LinkdList实现队列模式
队列模式 - 先进先出
while(!list.isEmpty()){ //String element = list.pollFirst(); String element = list.removeFirst(); System.out.println(element); }
栈模式 - 先进后出/后进先出
while(!list.isEmpty()){ String element = list.removeLast(); System.out.println(element); }
四、Vector与Stack
由于也是link下的分支,所以ArrayList的方法都能用。
独有方法:
Vector:
知识点:Vector老的方法
理解:Vector属于元老级别的集合类(JDK1.0),JDK1.2开始才有集合框架的概念,为了将Vector保留下来,Java的设计者让Vector多实现了List接口。public static void main(String[] args) { Vector<String> v = new Vector<>(); v.addElement("aaa"); v.addElement("bbb"); v.addElement("ccc"); v.addElement("ddd"); v.addElement("eee"); v.removeElementAt(0);//根据下标删除元素 v.removeElement("bbb");//根据数据删除元素 Enumeration<String> elements = v.elements(); while(elements.hasMoreElements()){ String nextElement = elements.nextElement(); System.out.println(nextElement); } }
Stack:
继承关系:class Stack extends Vector
特点:栈模式
LinkedList
和Stack
都能实现栈模式,但LinkedList
由于其实现了Deque
接口,因此更加灵活和现代化。在现代 Java 开发中,推荐使用LinkedList
或ArrayDeque
来实现栈,以替代传统的Stack
类。public static void main(String[] args) { Stack<String> stack = new Stack<>(); //将元素添加到栈顶 stack.push("aaa"); stack.push("bbb"); stack.push("ccc"); stack.push("ddd"); stack.push("eee"); System.out.println("获取栈顶元素:" + stack.peek()); System.out.println("获取元素到栈顶的距离:" + stack.search("bbb"));//4 - 从1开始数 //判断集合是否为空内容 while(!stack.empty()){ //删除栈顶元素,并返回 String pop = stack.pop(); System.out.println(pop); } }
五、HashSet与LinkedHashSet
HashSet的使用:
Link中所有和下标有关的方法均不可用。
HashSet set = new HashSet<>();
set.add(“abc”) - 添加数据
set.addAll(HashSet); - 将指定集合添加到集合的末尾
set.remove(“abc”) - 删除指定元素
set.removeAll(newSet); - 将list中有newSet的元素全部删除(删除交集)
set.retainAll(newset); - 将list中有newSet的元素保留,删除其他元素(保留交集)
int size = set.size(); - 获取元素的个数
set.clear(); - 清空集合里所有的元素
set.contains(“abc”) - 判断集合里是否包含指定元素,返回的是一个布尔值
set.containsAll(newSet); - 判断集合里是否包含指定集合,返回的是一个布尔值
set.isEmpty(); - 判断集合里有没有元素,返回的是一个布尔值
Object[] array1 = subSet.toArray();-将集合转换为数组
String[] array2 = subSet.toArray();- 将集合转换为指定类型的数组
遍历集合–foreach
for(String element:set){ System.out.println(element); }
遍历集合–Iterator
Iterator<String> it = set.iterator() while(it.hasNext()){ String next = it.next(); System.out.println(next); }
遍历集合 – ListIterator
ListIterator<String> listIterator = set.listIterator() while(listIterator.hasNext()){ String next = listIterator.next(); System.out.println(next); }
HashSet的特点
特点:无序且去重
添加步骤:
1.获取元素的hash值 – hashCode()
2.通过hash值计算在数组中的下标
3.判断下标上是否有元素
3.1 没有元素 – 直接添加
3.2 有元素 ---- 判断两个对象是否相同 hash && (==||equals())
3.2.1 相同 – 不添加数据(达到去重效果)
3.2.2 不相同 – 形成单向链表(JDK1.7头插法、JDK1.8尾插法)遍历步骤:
遍历数组(顺序遍历)注意:添加步骤处理数据的逻辑和遍历步骤处理数据的逻辑不一样,导致无序,但是哈希值相同的元素有序(在单向链表中)。
public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("aaa"); set.add("bbb"); set.add("ccc"); set.add("ccc"); set.add("Aa"); set.add("BB"); for (String element : set) { System.out.println(element);//aaa Aa BB ccc bbb } }
LinkedHashSet的使用:
同HashSet。
HashSet的特点
继承关系:class LinkedHashSet extends HashSet
特点:有序且去重添加步骤:
在父类HashSet的基础上,添加的元素会存储上一个元素的地址,
上一个元素也会存储下一个元素的地址 – 双向链表遍历步骤:
找到第一个元素,再向下依次找下一个元素注意:哈希值相同的元素依然存放在单向链表中,其余元素为双向。
public static void main(String[] args) { LinkedHashSet<String> set = new LinkedHashSet<>(); set.add("aaa"); set.add("bbb"); set.add("ccc"); set.add("ccc"); set.add("Aa"); set.add("BB"); for (String element : set) { System.out.println(element);//aaa bbb ccc Aa BB } }
六、TreeSet
TreeSet的使用
同HashSet.
TreeSet的特点
特点:自然排序(TreeSet会根据不同的类型使用不同的排序规则)
public static void main(String[] args) { //TreeSet存储String -> 字典排序 TreeSet<String> set1 = new TreeSet<>(); set1.add("a"); set1.add("c"); set1.add("e"); set1.add("bc"); set1.add("bb"); for (String element : set1) { System.out.println(element); } System.out.println("-----------------------------"); //TreeSet存储Integer -> 数字升序 TreeSet<Integer> set2 = new TreeSet<>(); set2.add(3); set2.add(5); set2.add(1); set2.add(2); set2.add(4); for (Integer element : set2) { System.out.println(element); } }
知识点:内置比较器
public static void main(String[] args) { TreeSet<Student> set = new TreeSet<>(); set.add(new Student("生希", '女', 29, "2402", "001")); set.add(new Student("名空", '女', 23, "2402", "002")); set.add(new Student("菜丽", '女', 25, "2402", "003")); set.add(new Student("桐光", '女', 30, "2402", "004")); set.add(new Student("岛玲", '女', 18, "2402", "005")); set.add(new Student("井步", '女', 19, "2402", "006")); set.add(new Student("奈奈", '女', 21, "2402", "007")); set.add(new Student("悠亚", '女', 21, "2402", "008")); set.add(new Student("朝阳", '女', 21, "2402", "009")); set.add(new Student("亮", '男', 21, "2402", "010")); set.add(new Student("从升", '男', 21, "2402", "011")); set.add(new Student("小康", '男', 21, "2402", "012")); set.add(new Student("伊织", '女', 21, "2403", "001")); set.add(new Student("美莉", '女', 21, "2403", "002")); set.add(new Student("咏美", '女', 21, "2403", "003")); set.add(new Student("爱蜜莉", '女', 21, "2403", "004")); set.add(new Student("绘里香", '女', 21, "2403", "005")); set.add(new Student("满里惠", '女', 21, "2403", "006")); set.add(new Student("Julia", '女', 21, "2403", "007")); set.add(new Student("亮", '男', 24, "2403", "008")); for (Student stu : set) { System.out.println(stu); } }
public class Student implements Comparable<Student>{ private String name; private char sex; private int age; private String classId; private String id; public Student() { } public Student(String classId, String id) { this.classId = classId; this.id = id; } public Student(String name, char sex, int age, String classId, String id) { this.name = name; this.sex = sex; this.age = age; this.classId = classId; this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getClassId() { return classId; } public void setClassId(String classId) { this.classId = classId; } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public boolean equals(Object obj) { if(this == obj){ return true; } if(obj instanceof Student){ Student stu = (Student) obj; if(classId.equals(stu.classId) && id.equals(stu.id)){ return true; } } return false; } @Override public String toString() { return name + "\t" + sex + "\t" + age + "\t" + classId + "\t" + id; } //排序规则:按照年龄排序 @Override public int compareTo(Student o) { return this.age - o.age; }
知识点:外置比较器
public class Test04 {
/**
- 知识点:外置比较器
*/
public static void main(String[] args) {TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() { //排序规则:按照名字长度排序,名字长度一致按照年龄排序 @Override public int compare(Student o1, Student o2) { if(o1.equals(o2)){ return 0; } int nameLen1 = o1.getName().length(); int nameLen2 = o2.getName().length(); if(nameLen1 != nameLen2){ return nameLen1 - nameLen2; } int age1 = o1.getAge(); int age2 = o2.getAge(); if(age1 != age2){ return age1 - age2; } return 1; } }); set.add(new Student("生希", '女', 29, "2402", "001")); set.add(new Student("名空", '女', 23, "2402", "002")); set.add(new Student("菜丽", '女', 25, "2402", "003")); set.add(new Student("桐光", '女', 30, "2402", "004")); set.add(new Student("岛玲", '女', 18, "2402", "005")); set.add(new Student("井步", '女', 19, "2402", "006")); set.add(new Student("奈奈", '女', 21, "2402", "007")); set.add(new Student("悠亚", '女', 21, "2402", "008")); set.add(new Student("朝阳", '女', 21, "2402", "009")); set.add(new Student("亮", '男', 21, "2402", "010")); set.add(new Student("从升", '男', 21, "2402", "011")); set.add(new Student("小康", '男', 21, "2402", "012")); set.add(new Student("伊织", '女', 21, "2403", "001")); set.add(new Student("美莉", '女', 21, "2403", "002")); set.add(new Student("咏美", '女', 21, "2403", "003")); set.add(new Student("爱蜜莉", '女', 21, "2403", "004")); set.add(new Student("绘里香", '女', 21, "2403", "005")); set.add(new Student("满里惠", '女', 21, "2403", "006")); set.add(new Student("Julia", '女', 21, "2403", "007")); set.add(new Student("亮", '男', 24, "2403", "008")); for (Student stu : set) { System.out.println(stu); } }
小结:Collection接口下的集合类的特点
List接口下的集合 ---------------------------------------------------
ArrayList
数据结构:一维数组
特点:存储数据LinkedList
数据结构:双向列表
特点:队列模式、栈模式Vector
数据结构:一维数组
特点:线程安全Stack
继承关系:class Stack extends Vector
特点:栈模式Set接口下的集合 ------------------------------------------------------
HashSet
数据结构:hash表(一维数组)
特点:无序且不可重复LinkedHashSet
继承关系:class LinkedHashSet extends HashSet
特点:有序且去重(添加了双向链表的数据结构)TreeSet
数据结构:二叉树
特点:根据元素类型自然排序