22_23集合【Collection家族(list,set)、泛型、迭代器及其底层原理*、比较器接口】

集合

学习过程

概念 使用(熟悉API) 深入底层原理 项目中的应用

初识

集合就是一组数据的容器

存放多个数据–> 数组、集合

有些集合专门用于存放数据

有些集合专门用去排序

有些集合专门去重

集合框架图

标红为已经学习的内容,其他内容待学习补充
集合框架图

含义

  1. 集合是Java API所提供的一系列类,可以用于动态存放多个对象 (集合只能存对象)
  2. 集合与数组的不同在于,集合是大小可变的序列,而且元素类型可以不受限定,只要是引用类型。(集合中不能放基本数据类型,但可以放基本数据类型的包装类)
  3. 集合类全部支持泛型,是一种数据安全的用法。

集合与数组的不同

数组:一旦初始化后长度不可变,元素类型受限定(String类型的数组只能装String的数据),数组可以存储基本数据类型

集合:长度可变的序列,元素类型不受限定(一个集合可以存储多个数据类型的元素),集合只能存储引用数据类型

Collection家族

规范了添加删除

List接口

特点:有序且可重复(因为List接口中添加了许多针对下标操作的方法)

实现类:

  1. ArrayList
  2. LinkedList
  3. Vector
  4. Stack
1.ArrayList

接触ArrayList

感受一个集合可以存储多个类型的数据

经验:平时不会将多个数据类型的数据存入一个集合中,因为不好管理

  ArrayList list = new ArrayList();
  
  list.add(100);//Integer.valueOf(100)
  list.add(123.123);//Double.valueOf(123.123)
  list.add("abc");

使用

注意:集合一旦定义了泛型,就规定了集合存储元素的类型,所以说泛型是一种数据安全的做法

	public static void main(String[] args) {
		
		//创建ArrayList集合的对象
		ArrayList<String> list = new ArrayList<>();
		
		//添加元素
		list.add("麻生希");
		list.add("椎名空");
		list.add("水菜丽");
		list.add("朝桐光");
		list.add("三上悠亚");
		list.add("水野朝阳");
		list.add("古川伊织");
		
		//设置指定下标上的元素
		list.set(1, "奇男子");
		
		//获取指定下标上的元素
		String str = list.get(1);
		System.out.println("获取指定下标上的元素:" + str);//奇男子
		
		//获取元素个数
		int size = list.size();
		System.out.println("获取元素个数:" + size);//7
		
		//将元素添加到指定下标的位置
		list.add(4, "北条麻衣");
		
		ArrayList<String> newList1 = new ArrayList<>();
		Collections.addAll(newList1, "aaa","bbb","ccc","ccc");//利用Collections工具类给集合做批量添加
		list.addAll(newList1);//将newList1中所有的元素添加到list集合的末尾
		
		ArrayList<String> newList2 = new ArrayList<>();
		Collections.addAll(newList2, "xxx","yyy","zzz","zzz");//利用Collections工具类给集合做批量添加
		list.addAll(4, newList2);//将newList2中所有的元素添加到list集合指定下标的位置
		
		//清空集合中所有的元素
		//list.clear();
		
		System.out.println("判断集合中是否包含指定元素:" + list.contains("奇男子"));//true
		System.out.println("判断集合中是否包含子集合中所有的元素:" + list.containsAll(newList1));//true
		
		System.out.println("获取集合中第一次出现该元素的下标:" + list.indexOf("ccc"));//13
		System.out.println("获取集合中最后一次出现该元素的下标:" + list.lastIndexOf("ccc"));//14
		
		System.out.println("判断集合中是否没有元素:" + list.isEmpty());//false
		
		list.remove(0);//根据下标删除元素
		list.remove("朝桐光");//根据元素删除元素
		list.removeAll(newList1);//删除list中包含newList1的元素(去除交集)
		
		list.retainAll(newList2);//保留list中包含newList2的元素(保留交集)
		
		List<String> subList = list.subList(1, 3);//截取开始下标(包含)到结束下标(排他)处的元素,返回新的List集合
		
		//将集合转成数组
		Object[] objs = subList.toArray();
		System.out.println(Arrays.toString(objs));
		
		//将集合转成数组
		String[] ss = new String[2];
		subList.toArray(ss);
		System.out.println(Arrays.toString(ss));
		
		System.out.println("--------------------------------");
		
		//遍历数据 -- for循环
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
		
		System.out.println("--------------------------------");
		
		//遍历数据 -- foreach
		for (String element : list) {
			System.out.println(element);
		}
		
		System.out.println("--------------------------------");
		
		//遍历数据 -- Iterator
		Iterator<String> it = list.iterator();
		while(it.hasNext()){//判断是否有可迭代的元素
			
			String element = it.next();//返回下一个元素
			System.out.println(element);
		}
		
		System.out.println("--------------------------------");
		
		//遍历数据 -- ListIterator
		ListIterator<String> listIterator = list.listIterator();
		while(listIterator.hasNext()){//判断是否有可迭代的元素
			
			String element = listIterator.next();//返回下一个元素
			System.out.println(element);
		}

day23

2.LinkedList

LinkedList和ArrayList使用是一模一样的,这要归功于Conllection和List接口
因为接口是定义标准的!!!
只不过LinkedList和ArrayList底层实现原理是不一样的,LinkedList底层使用双向链表去实现对于数据的操作(增删改查)

LinkedList独有的方法
		LinkedList<String> list = new LinkedList<>();
		
		list.add("麻生希");
		list.add("abc");
		list.add("椎名空");
		list.add("abc");
		list.add("水菜丽");
		list.add("abc");
		list.add("朝桐光");
		
		//将元素添加到首部
		list.addFirst("奇男子");
		list.offerFirst("小小");
		list.push("大大");
		
		
		//将元素添加到尾部
		list.addLast("xxx");
		list.offerLast("yyy");
		list.offer("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.getLast());
		System.out.println("获取最后一个元素:" + list.peekLast());
		
		list.pop();//删除第一个元素
		list.poll();//删除第一个元素
		list.pollFirst();//删除第一个元素
		list.removeFirst();
		
		list.pollLast();//删除最后一个元素
		list.removeLast();//删除最后一个元素		
		
		list.removeFirstOccurrence("abc");//删除第一次出现的元素
		list.removeLastOccurrence("abc");//删除最后一个出现的元素
		
		//倒序遍历
//		Iterator<String> descendingIterator = list.descendingIterator();
//		while(descendingIterator.hasNext()){//hasNext()底层是判断是否有上一个元素
//			String element = descendingIterator.next();//next()底层是获取上一个元素
//			System.out.println(element);
//		}
		
		System.out.println("----------------------------");
		
		for (String element : list) {
			System.out.println(element);
		}
LinkedList 的 队列模式(先进先出)

代码添加、删除元素形式呈现模式,体会其特征

		LinkedList<String> list = new LinkedList<>();
		
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		list.add("ddd");
		list.add("eee");
		
//		遍历
//		for (String element : list) {
//			System.out.println(element);
//		}
		
		//取出来
		while(!list.isEmpty()){
			
			String element = list.removeFirst();//删除第一个元素,并返回
			System.out.println(element);
			
		}
		
		System.out.println("获取元素个数:" + list.size());//0
	}
LinkedList 的 栈模式(先进后出 或 后进先出)
		while(!list.isEmpty()){
			
			String element = list.removeLast();//删除最后一个元素,并返回
			System.out.println(element);
			
		}
3.Vector

Vector:ArrayList的数据结构一致(一维数组),但是Vector是线程安全的(加锁)
Vector和ArrayList使用是一模一样的,这要归功于Conllection和List接口

因为接口是定义标准的!!!

只不过Vector和ArrayList底层实现原理是不一样的,Vector底层使用一维数组+线程安全去实现对于数据的操作(增删改查)

知识点:研究Vector的老方法
Vector的历史:
Vector是元老级别的类(JDK1.0),集合框架是JDK1.2开始才推出的概念,
当时大部分程序员已经习惯Vector去存储数据,为了更好的推广集合框架和保留Vector,Java让Vector又多实现了List接口

经验:老的方法大多数使用element这个单词

		Vector<String> v = new Vector<>();
		
		v.addElement("aaa");
		v.addElement("bbb");
		v.addElement("ccc");
		v.addElement("ddd");
		v.addElement("eee");
		
		//清空
		//v.removeAllElements();
		
		v.removeElementAt(0);//根据下标删除元素
		v.removeElement("ccc");//根据元素删除元素
		
		v.setElementAt("奇男子", 1);//将元素设置到指定下标的位置
		
		Enumeration<String> elements = v.elements();
		while(elements.hasMoreElements()){
			
			String element = elements.nextElement();
			System.out.println(element);
		}
4.Stack

知识点:研究Stack的特点 – 栈模式(先进后出)

注意:该类继承Vector,所以Stack可以使用父类所有的非私有的方法

Stack特有的方法
		Stack<String> stack = new Stack<>();
		
		//将元素添加到栈顶
		stack.push("111");
		stack.push("222");
		stack.push("333");
		stack.push("444");
		stack.push("555");
		
		System.out.println("获取栈顶元素:" + stack.peek());//555
		
		int search = stack.search("222");
		System.out.println("获取元素距离栈顶的个数(基于1):" + search);//4
		
		//遍历取出元素
		while(!stack.empty()){
			
			//删除栈顶元素,并返回
			String element = stack.pop();
			System.out.println(element);
		}
Set接口

特点:无序且不可重复(没有下标操作)

对于存取

“aaa”,“bbb”,“ccc” | “ccc”,“aaa”,“bbb” 无序不是随机

实现类:

  1. HashSet
  2. LinkedHashSet
  3. TreeSet
1.HashSet
使用
		//创建HashSet集合的对象
		HashSet<String> set = new HashSet<>();
		
		//添加元素
		set.add("麻生希");
		set.add("椎名空");
		set.add("水菜丽");
		set.add("朝桐光");
		set.add("三上悠亚");
		set.add("水野朝阳");
		set.add("古川伊织");
		set.add("xxx");
		set.add("yyy");
		set.add("zzz");
		
		//获取元素个数
		int size = set.size();
		System.out.println("获取元素个数:" + size);//10
		
		HashSet<String> newSet1 = new HashSet<>();
		Collections.addAll(newSet1, "aaa","bbb","ccc","ccc");//利用Collections工具类给集合做批量添加
		set.addAll(newSet1);//将newSet1中所有的元素添加到set集合的末尾
		
		//清空集合中所有的元素
		//set.clear();
		
		System.out.println("判断集合中是否包含指定元素:" + set.contains("奇男子"));//true
		System.out.println("判断集合中是否包含子集合中所有的元素:" + set.containsAll(newSet1));//true
		
		System.out.println("判断集合中是否没有元素:" + set.isEmpty());//false
		
		set.remove("朝桐光");//根据元素删除元素
		set.removeAll(newSet1);//删除set中包含newset1的元素(去除交集)
		
		HashSet<String> newSet2 = new HashSet<>();
		Collections.addAll(newSet2, "xxx","yyy","zzz","zzz");//利用Collections工具类给集合做批量添加
		set.retainAll(newSet2);//保留set中包含newset2的元素(保留交集)
		
		//将集合转成数组
		Object[] objs = set.toArray();
		System.out.println(Arrays.toString(objs));
		
		//将集合转成数组
		String[] ss = new String[set.size()];
		set.toArray(ss);
		System.out.println(Arrays.toString(ss));
		
		System.out.println("--------------------------------");
		
		//遍历数据 -- foreach
		for (String element : set) {
			System.out.println(element);
		}
		
		System.out.println("--------------------------------");
		
		//遍历数据 -- Iterator
		Iterator<String> it = set.iterator();
		while(it.hasNext()){//判断是否有可迭代的元素
			
			String element = it.next();//返回下一个元素
			System.out.println(element);
		}
特点

数据结构:一维数组+单向链表
特点:无序 + 去重

无序的原因:存入顺序的规则和取出顺序的规则不一致

去重的原因:元素的hash值相同,再判断hash&&==||equals,如果相同就不存入到集合中

		HashSet<String> set = new HashSet<>();
		
		set.add("aaa");
		set.add("bbb");
		set.add("ccc");
		set.add("ddd");
		set.add("ddd");
		set.add("Aa");
		set.add("BB");
		
		for (String element : set) {
			System.out.println(element);
		}
HashSet理解图

存在hash值相同的情况:字符串"BB"和"Aa"的哈希值是相同的
HashSet理解图

2.LinkedHashSet
使用

LinkedHashSet 继承 HashSet

特点

数据结构:一维数组+单向链表+双向链表
特点:有序 + 去重

有序的原因:在HashSet的基础上添加了双向链表

去重的原因:元素的hash值相同,再判断hash&&==||equals,如果相同就不存入到集合中

LinkedHashSet理解图

LinkedHashSet理解图

泛型

含义:数据安全的做法

泛型限定:

?表示什么类型都可以

? extends A 表示元素必须是A类或A的子类

? super A 表示元素必须是A类或A的父类

public class Test01 {
	public static void main(String[] args) {
	}
	
	//?表示任何类型
	public ArrayList<?> method01(){
		//jdk1.7之后对象的泛型可以不写类型,在引用写了可以代表
		//ArrayList<Object> list = new ArrayList<>();
		//ArrayList<String> list = new ArrayList<>();
		//ArrayList<Integer> list = new ArrayList<>();
		//ArrayList<A> list = new ArrayList<>();
		ArrayList<B> list = new ArrayList<>();
		
		return list;
	}
	
	//? extends A 表示A类或其子类
	public ArrayList<? extends A> method02(){
		
		//ArrayList<A> list = new ArrayList<>();
		ArrayList<B> list = new ArrayList<>();
		
		return list;
	}
	
	//? super A 表示A类或其父类
	public ArrayList<? super A> method03(){
		
		//ArrayList<A> list = new ArrayList<>();
		ArrayList<Object> list = new ArrayList<>();
		
		return list;
	}
}

E - element - 元素

T - type - 类型

K - key - 键

V - value - 值

注意:泛型可以设置多个,使用逗号隔开

ps:public class HashMap<K,V>{}

泛型在项目中的应用

需求:编写通用的管理系统的接口

分析:

学生管理系统 – 管理学生

淘宝后台管理系统 – 管理用户

OA管理系统 – 管理员工

//管理系统的接口
public interface ManagerSystem<T> {

	public int add(T t);
	
	public int delete(T t);
	
	public int update(T t,int type,Object val);
	
	public int getElementIndex(T t);
	
	public T getElement(T t);
	
	
}

例如Book类的 BookManagerSystem实现ManagerSystem接口

public class BookManagerSystem<T> implements ManagerSystem<T> {//外部new对象传入类型

	@Override
	public int add(T t) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int delete(T t) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int update(T t, int type, Object val) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getElementIndex(T t) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public T getElement(T t) {
		// TODO Auto-generated method stub
		return null;
	}

}
public class Test01 {

	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<String> bms1 = new BookManagerSystem<>();
		bms1.add("Java从入门到精通");
		bms1.add("MySQL从删库到跑路");
		
		//图书管理系统 -- 管理的是图书类别
		BookManagerSystem<String> bms2 = new BookManagerSystem<>();
		bms2.add("小说类");
		bms2.add("科技类");
		bms2.add("历史类");
	}
}

迭代器

含义:遍历集合中的数据

分类:Iterator 和 ListIterator

Iterator 和 ListIterator 区别

Iterator :Collection接口下所有的实现类都可以获取的迭代器,可以在遍历时删除元素

ListIterator :List接口下所有的实现类可以获取的迭代器,可以在遍历时删除、替换、添加元素,也可以指定下标开始遍历,还可以倒叙遍历

注意:ListIterator继承Iterator

需求:
Iterator

ps

使用Iterator遍历元素,遍历到“bbb”时删除该元素

不能使用ArrayList删除元素的原因:Iterator遍历不知道ArrayList删除元素,数据不统一;注意区分删除功能到底属于ArrayList还是Iterator

		ArrayList<String> list = new ArrayList<>();
		
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		list.add("ddd");
		
		Iterator<String> it = list.iterator();
		while (it.hasNext()) {
			String element = it.next();
			if(element.equals("bbb")){
				//注意:在Iterator遍历时,不能使用list.remove(),会报错(内外操作数不一致)
				//list.remove(element);
				
				//注意:在Iterator遍历时,如果要删除元素,使用迭代器中的remove()
				//思路:1.告诉ArrayList需要删除的元素 2.迭代器内部在更新现有数据
				it.remove();
			}
		}
		
		for (String element : list) {
			System.out.println(element);
		}

游标依靠顺序开始遍历,删除元素

Iterater内–重写remove(借助ArraList的remove(下标))后,下标赋值给游标,下次遍历从删除下标开始;统一内外操作数,把外部操作数赋值给内部操作数

【Iterater没有删除元素的功能,依赖于ArraList的remove(元素)】

底层实现:见【研究Iterator遍历ArrayList集合的底层原理】

ListIterator

ps

使用ListIterator指定下标遍历元素

		ArrayList<String> list = new ArrayList<>();
		
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		list.add("ddd");
		
		ListIterator<String> listIterator = list.listIterator(2);
		while(listIterator.hasNext()){
			String element = listIterator.next();
			System.out.println(element);
		}

使用ListIterator倒序遍历元素

		ListIterator<String> listIterator = list.listIterator(list.size());//把指针移动到最后
		while(listIterator.hasPrevious()){//判断是否有上一个元素
			
			String element = listIterator.previous();//获取上一个元素
			System.out.println(element);
迭代器底层

学习迭代器底层目的:深入理解面向对象的设计思想,应付面试

1.手撕Iterator底层源码
(1).研究foreach/增强for循环

知识点:研究foreach/增强for循环

注意:foreach底层是由Iterator实现

if、for等的代码块只有一个语句,可省略{},但可读性并不是很好

	public static void main(String[] args) {	
		ArrayList<String> list = new ArrayList<>();
		
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		list.add("ddd");
		
		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();
		//初始化变量,判断条件,执行里面代码块
        
		//注意:
		//	1.泛型只在编码阶段有效,不会编译到class文件中
		//	2.泛型只支持引用数据类型(基本数据类型只能用包装类)	
	}
(2).研究Iterator遍历ArrayList集合的底层原理

知识点:研究Iterator遍历ArrayList集合的底层原理

注意:

研究底层原理,就势必会看底层源码

底层源码必须找场景去研究,不能从上往下去理解(会越看越乱码)

源码

//场景
ArrayList<String> list = new ArrayList<>();
		
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");

Iterator<String> it = list.iterator();//《1》《2》
//(多态)ArrayList里的iterator方法返回接口,接口不能有对象,所有new了一个实现类对象
while (it.hasNext()) {
    String element = it.next();
    System.out.println(element);
}

查看AbstractList底层,重要属性modCount

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    
    //外部操作数
    //作用:记录修改元素的次数(添加、删除会让该变量++)
    protected transient int modCount = 0;//modCount - 4+1=5(删除一个元素+1=5(ArrayList底层remove方法))
}

思考题:为什么添加和删除才会把modCount++ ;为什么修改和查询不会把modCount++?

集合用于存储数据、管理数据 -- 增删改查 

​ 进行添加或删除操作时,会改变集合的元素个数

学生管理系统为arilist底层

ArrayList添加元素的过程:

1.判断是否扩容(modCount++)

2.将元素添加到一维数组指定下标上的位置

ArrayList删除元素的过程:

添加元素

添加元素外部操作数指针
modCountsize
00
第一个11
第二个22
第三个33
第四个44

已经添加四个元素

遍历(没有增删)游标当前元素的下标外部操作数内部操作数
cursorlastRetmodCountexpectedModCount
0-144
第一次10
第二次21
第三次32
第四次43

遍历时删除元素’‘bbb’‘

遍历(没有增删)游标当前元素的下标外部操作数内部操作数
cursorlastRetmodCountexpectedModCount
0-144
第一次10
第二次21
删除’‘bbb’‘1-14+1=54==5
第三次2
public class ArrayList<E> extends AbstractList<E> implements List<E>{
    //添加元素
    //元素个数
    private int size;//size - 4(添加结束)
    //数据容器 - ["aaa","bbb","ccc","ddd",null,null,null,null,null,null]
    transient Object[] elementData;
    
    //添加元素,e - ddd(添加结束)
    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++;//添加一个元素+1
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    //ArrayList底层remove方法
    public E remove(int index) {
        rangeCheck(index);
		//利用ArraList类的remove()去删除元素,外部操作数+1
        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }
    
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    
    //ArrayList类的内部类 -- 实现了遍历元素的功能《2》
    private class Itr implements Iterator<E> {
        
        int cursor;       // 游标 - 0+1+1+1+1=4
        int lastRet = -1; // 当前元素的下标 - -1+1+1+1+1=3
        int expectedModCount = modCount;//内部操作数 - 4==5(删除一个元素外部操作数+1=5(ArrayList底层remove方法),重写的remove方法重新把外部操作数赋值给内部操作数)
		
        //判断是否有可迭代的元素
        public boolean hasNext() {
            return cursor != size;//4 - 4(遍历结束)
        }

        @SuppressWarnings("unchecked")
        public E next() {
            
            /**
            	思考题:为什么在获取元素时,会先判断外部操作数是否等于内部操作数
            		考虑到遍历元素时,如果添加或删除元素,会导致数据个数变化
            		遍历时就有可能出现脏数据,如果外部操作数和内部操作数不相同就以为数据不统一,就报错!
            */
            checkForComodification();//判断外部操作数是否等于内部操作数,如果不等于就报错
            int i = cursor;//i - 0+1+1+1
            if (i >= size)
                throw new NoSuchElementException();
            //获取外部类的成员属性 -- elementData
            Object[] elementData = ArrayList.this.elementData;//成员内部类访问外部类属性
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            
            //elementData -- 获取的是ArrayList(外部类的)elementData -> Object[] elementData;
            //Object类型的元素强转为集合中真实类型的元素
            // --> elementData[3] --> Object类型的数据需要强转为String类型
            return (E) elementData[lastRet = i];//elementData[3]
        }
        
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

        //重写了Iterator接口中的remove()
        public void remove() {
            //判断当前元素的下标是否小于0
            if (lastRet < 0)
                throw new IllegalStateException();
            
            //判断外部操作数是否和内部操作数一致,不一致就会报错
            checkForComodification();

            try {
                //利用ArraList类的remove()去删除元素
                ArrayList.this.remove(lastRet);
                //把当前元素的下标赋值给游标
                cursor = lastRet;
                //把-1赋值给lastRet
                lastRet = -1;
                //重新把外部操作数赋值给内部操作数
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        
    }
    
}

查看Iterator底层

/**
	
	思考题:为什么迭代器是一个接口,不是类???
		因为Java提供了很多的集合,不同的集合实现增删改查的原理是不一样的,
		所以,不同的集合都实现了各自遍历元素的代码(实现了各自的迭代器)
*/
//迭代器的接口《1》
public interface Iterator<E> {

    //判断是否有可迭代的元素
     boolean hasNext();

     //获取下一个元素
     E next();

     //删除元素的默认方法(作用:给实现类去重写,如果实现类可以选择不重写,意味着遍历时不能删除元素,当然实现类可以选择重写,那么就意味着这个迭代器可以在遍历元素时删除元素)
     default void remove() {
         //抛出异常 -- 删除异常
         throw new UnsupportedOperationException("remove");
     }
}
2.手撕ListIterator底层源码

ListIterator继承Iterator,部分底层代码同上略


   // ListIterator原理实现
    public ListIterator<E> listIterator() {
        return new ListItr(0);//0 - 设置游标的位置
    }
    
    //指定游标位置
    public ListIterator<E> listIterator(int index) {
        return new ListItr(index);
    }
    
    private class ListItr extends Itr implements ListIterator<E> {
        
        ListItr(int index) {
            super();
            cursor = index;
        }

        //判断是否有上一个元素
        public boolean hasPrevious() {
            return cursor != 0;
        }

        //获取下一个元素的下标
        public int nextIndex() {
            return cursor;
        }

        //获取上一个元素的下标
        public int previousIndex() {
            return cursor - 1;
        }

        //获取上一个元素
        @SuppressWarnings("unchecked")
        public E previous() {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[lastRet = i];
        }

        //设置元素到当前下标上
        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                //ListIterator的set功能还是依赖于ArrayList
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        //添加元素到当前下标上
        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                //ListIterator的add功能还是依赖于ArrayList
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }
}

比较器接口

作用:排序时使用

分类:

​ 内置比较器:Comparable - compareTo()

​ 外置比较器:Comparator - compare()

使用场景:

​ 内置比较器:对象要想存入TreeSet、TreeMap中,对象所属的类必须要实现内置比较器

​ 外置比较器:当内置比较的规则不满足现在的需求,但又不能改动内置比较器规则时

优先级别:外置比较器 > 内置比较器

注意

  1. Collection 与 Map的区别

Collection 存单个值,可以获取迭代器进行遍历

Map存两个值(Key-Value)【键值对】,不可以获取迭代器,不能遍历(Map可以间接遍历)

  1. 理解Set为什么是无序

无序:存入顺序和取出顺序不一致,无序不等于随机

  1. ArrayList 与 LinkedList的区别

使用上的区别:

​ LinkedList添加了

​ 队列模式-先进先出(removeFirst())

​ 栈模式-先进后出(removeLast())

效率上的区别:

ArrayList底层数据结构是一维数组

LinkedList底层数据结构是双向链表

​ 添加 - 不扩容的情况:ArrayList快

​ 添加 - 扩容的情况:LinkedList快

​ 删除:LinkedList快

​ 查询:ArrayList快

​ 修改:ArrayList快

​ 注意:工作中常用ArrayList,因为很多需求都需要使用查询功能,ArrayList查询更快

  1. 各种集合的应用场景

ArrayList:存数据,线程不安全

LinkedList:队列模式、栈模式,线程不安全

Vector:弃用,线程安全

Stack:弃用,线程安全

HashSet:去重+无序,线程不安全

LinkedHashSet:去重+有序,线程不安全

TreeSet:排序,线程不安全

HashMap:存key+value,key去重,无序,线程不安全

LinkedHashMap:存key+value,key去重,有序,线程不安全

Hashtable:弃用,存key+value,key去重,无序,线程安全,方法加锁-效率低

ConcurrentHashMap:存key+value,key去重,无序,线程安全,局部加锁、CAS-效率高

TreeMap:存key+value,针对于Key排序

Properties:配置文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值