Java集合
【1】数组、集合都是对多个数据进行存储操作的,简称为容器。
这里的存储指的是在内存层面的存储,而不是持久化存储(不是存储在硬盘)
【2】数组的特点:
- 
数组一旦指定长度后,是不能修改的。 
- 
数组一旦声明数组类型后,只能存放这个类型的元素。 
【3】数组的缺点:
- 数组不能修改长度
- 插入、删除元素效率低
- 数组中实际元素的长度无法获取,没有提供相应的方法
- 数组存储是有序的,可重复的。对于无序的,不可重复的数据需求,数组不能满足要求。
【4】因为这些缺点,所以引入了一个新的数据结构--> 集合
【5】为什么要学习这么多集合?
不同集合的底层数据结构不一样。集合不一样,特点也就不一样。
集合结构图

集合应用场景
前后端数据库交互:

当需要将相同结构的个体整合到一起的时候,需要用到集合:
实际应用场景:


Collection接口
集合有一个特点:只能存放引用类型的数据,不能存放基本类型的数据
Collection接口常用方法
add()、addAll()、clear()、remove()、iterator()、size()、contains()、equals()、isEmpty()
package com.collection.demo01;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class Demo01 {
    public static void main(String[] args) {
        /*
        Collection接口的常用方法:
        增加:add(E e)、addAll(Collection<? extends E> c)
        删除:clear() 、remove(Object o)
        修改:
        查看:iterator() 、size()
        判断:contains(Object o) 、equals(Object o)、isEmpty()
         */
        //创建对象:接口不能创建对象,但可以利用实现类创建对象
        Collection col = new ArrayList();
        //调用方法:
        //集合有一个特点:只能存放引用类型的数据,不能存放基本类型的数据
        //基本数据类型自动装箱为对应的包装类。int --> Integer
        col.add(20); //调用的是Collection接口的add方法
        col.add(14);
        col.add(34);
        col.add(56);
        System.out.println(col); //本质上调用了toString方法
//        System.out.println(col.toString());
        List list = Arrays.asList(new Integer[]{12, 34, 56, 87, 45}); //转换成List集合
        col.addAll(list); //将另一个集合添加到col集合中
        System.out.println(col);
        col.clear(); //清空集合
        System.out.println(col);
        System.out.println("集合中的元素数量:" + col.size());
        System.out.println("集合是否为空:" + col.isEmpty());
        boolean isRemove = col.remove(56);
        System.out.println(col);
        System.out.println("集合中数据是否被删除:" + isRemove);
        Collection col2 = new ArrayList();
        col2.add(20);
        col2.add(14);
        col2.add(34);
        col2.add(56);
        Collection col3 = new ArrayList();
        col3.add(20);
        col3.add(14);
        col3.add(34);
        col3.add(56);
        System.out.println(col2.equals(col3)); //true,比较集合里的内容是否相同
        System.out.println(col2 == col3); //false,地址一定不相等
        System.out.println("是否包含元素:" + col3.contains(20));
    }
}
集合遍历
迭代器简要原理图:

代码实现:
package com.collection.demo01;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo02 {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(20);
        col.add(14);
        col.add(34);
        col.add(56);
        col.add("abc");
        col.add(123.423);
        //对集合遍历(查看集合中的元素)
        //方法1:普通for循环(不行)
//        for (int i = 0; i < col.size(); i++) {
//            System.out.println(col[i]); //集合不能用下标访问到,因为Collection没有索引
//        }
        //方法2:增强for循环
        for (Object o : col) {
            System.out.println(o);
        }
        System.out.println("-------------");
        //方法3:iterator()
        Iterator it = col.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}
List接口
List接口继承了Collection接口,是Collection接口的子接口。父类的属性和方法它全都有
List接口常用方法
增加:add(int index, E element)
删除:remove(int index) 、remove(Object o)
修改:set(int index, E element)【新增】
查看:get(int index)【新增】
判断:
package com.collection.demo01;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo03 {
    public static void main(String[] args) {
        /*
        List接口中常用方法:
        增加:add(int index, E element)
        删除:remove(int index) 、remove(Object o)
        修改:set(int index, E element)【新增】
				查看:get(int index)【新增】
        判断:
         */
        //List接口不能创建对象,但可以利用它的实现类创建对象
        List list = new ArrayList();
        list.add(13); //基本数据类型自动装箱成对应的包装类
        list.add(19);
        list.add(24);
        list.add(-1);
        list.add(3);
        list.add("abc");
        System.out.println(list);
        list.add(3, 77); //在索引为3的位置(第4个元素)添加元素77
        System.out.println(list);
        list.set(3,55); //修改索引为3的元素(第4个元素)为55
        System.out.println(list);
        list.remove(3); //删除索引为3的元素,在集合中传入int类型数据的时候,remove方法调用的是:remove(int index)
        System.out.println(list);
        list.remove("abc"); //删除内容是abc的元素,必须用双引号括起来
      	System.out.println(list);
      	//list.remove(3); //错误,删除的是索引为3的元素,而不是内容为3的元素,调用的是remove(int index)方法
        Object o = list.get(0); //获取索引为0的元素,元素的返回类型是Object
        System.out.println(o);
        //List集合遍历:
        //方法1:普通for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i)); //List可以用for循环是因为它有索引,这样就可以找到对应的集合元素了
        }
        System.out.println("-----------");
        //方法2:增强for循环
        for (Object obj : list) {
            System.out.println(obj);
        }
        System.out.println("-----------");
        //方法3:迭代器iterator
        Iterator it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}
ArrayList实现类(JDK1.7)❌
【1】在IDEA中切换JDK版本的方法:

【2】ArrayList实现List接口的失误:
写集合的作者承认了这个失误,但是在后续版本中并没有删除,他觉得没必要:

【3】ArrayList底层重要属性:
底层是Object数组

在JDK1.7中,调用构造器时,给底层数组elementData初始化,数组初始化长度为10:

内存分析:

调用add方法:
ArrayList al = new ArrayList();
System.out.println(al.add("abc"));
System.out.println(al.add("def"));

当数组中的10个位置都满了的时候,就开始对数组进行扩容,扩容长度是原数组的1.5倍:



ArrayList实现类(JDK1.8)
【1】JDK1.8底层依然是Object类型数组,size:数组中元素的有效长度。

【2】调用空构造器:
ArrayList al = new ArrayList(); 调用空构造器,数组长度为0,即空。

【3】调用add方法:





Vector
【1】Vector底层是Object数组,int类型属性表示数组中元素的有效长度:

【2】调用构造器:
Vector v = new Vector();

【3】add方法:
数组满了后扩容是原数组的2倍

泛型(重点)
【1】什么是泛型?
泛型就相当于标签
格式:<>
集合容器类在设计阶段/声明阶段不能确定这个容器实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决,因为这个时候除了元素的类型不确定,其他部分是确定的,例如关于这个元素如何保存,如何管理是确定的,因此把元素的类型设计成一个参数,这个类型参数叫做泛型。
Collection
【2】没有使用泛型的集合:
package com.collection.demo03;
import java.util.ArrayList;
public class Demo01 {
    public static void main(String[] args) {
        //创建一个ArrayList对象,向这个集合中存入学生成绩
      	//没有使用泛型,集合的缺点:什么类型数据都可以放进集合,不方便管理,可读性差,不能让人快速了解集合存放的数据类型
        ArrayList al = new ArrayList();
        al.add(98);
        al.add(23);
        al.add(43);
        al.add(65);
        al.add(87);
        al.add("小白"); //混入了一个String类型的错误数据,什么类型数据都可以放进集合,是不合理的!
        //对集合遍历
        for (Object o : al) {
            System.out.println(o);
        }
    }
}
如果不使用泛型的话,有缺点:
一般我们在使用集合的时候,往集合中存入的都是相同类型的数据以便于管理,所以现在什么引用数据类型都可以存入集合,不方便。
解决方案:集合加入泛型,限制放入集合的引用类型
【3】在集合中使用泛型
在JDK1.5以后加入泛型
加入泛型的优点:在编译期会对类型进行检查,不是泛型对应的类型就不可以加入这个集合
package com.collection.demo03;
import java.util.ArrayList;
public class Demo01 {
    public static void main(String[] args) {
        //创建一个ArrayList对象,向这个集合中存入学生成绩
        //加入泛型的优点:在编译期会对类型进行检查,不是泛型对应的类型就不可以加入这个集合
        ArrayList<Integer> al = new ArrayList<>();
        al.add(98);
        al.add(23);
        al.add(43);
        al.add(65);
        al.add(87);
//        al.add("小白"); //报错,集合已加入泛型,String类型不符合集合泛型要求
//        //对集合遍历
//        for (Object o : al) { //有了泛型就不要再用Object了,不安全!
//            System.out.println(o);
//        }
        for (Integer i : al) { //推荐使用集合的泛型类型,遍历集合元素
            System.out.println(i);
        }
    }
}
【4】泛型总结:
- JDK1.5以后加入泛型
- 泛型实际就是一个用<>引起来的 参数类型,这个参数类型在使用的时候才会确定具体的类型。

- 
使用了泛型以后,可以确定集合中存放的数据类型,在编译期就能检查出来。 
- 
使用泛型是为了方便后续的遍历等操作,减轻它们的负担。 
- 
泛型的类型都是引用数据类型,不能是基本数据类型。 
泛型类、泛型接口
【1】泛型类的定义和实例化:
泛型类的定义:
- Demo02是一个普通的类
- 而Demo02是一个泛型类 
- <>里面就是一个参数类型,但是这个类型是什么呢?这个类型现在是不确定的,只有在编译器才能确定,现在相当于一个占位符
- 现在确定的是这个类型一定是一个引用数据类型,而不是基本数据类型!
- 泛型类中的方法只能是泛型方法,不可以是普通方法,但是属性可以用泛型属性,也可以是普通变量
package com.collection.demo03;
/*
 * Demo02是一个普通的类
 * 而Demo02<E>是一个泛型类
 * <>里面就是一个参数类型,但是这个类型是什么呢?这个类型现在是不确定的,只有在编译器才能确定,现在相当于一个占位符
 * 现在确定的是这个类型一定是一个引用数据类型,而不是基本数据类型!
 */
//泛型类
//泛型E可以作参数,也可以作变量类型、数组类型
public class Demo02<E> {
    int age;
    String name;
    E sex; //泛型作变量类型
  	//泛型E(变量类型)作参数
    public void a(E n) {
    }
  	//泛型E[](数组类型)作参数
    public void b(E[] m) {
    }
  
//    //泛型类中的方法只能是泛型方法,不可以是普通方法,但是属性可以用泛型属性,也可以是普通变量
//    public void c(int i) {
//
//    }
  
}
class Demo02Test {
    public static void main(String[] args) {
        //对Demo02进行实例化
        //(1)实例化的时候不指定泛型:如果实例化的时候不指定类的泛型,那么泛型默认为Object类型
        Demo02 d1 = new Demo02(); //没有指定泛型,默认为Object类型
        d1.a("abc");
        d1.a(18);
        d1.a(9.2);
        d1.b(new String[] {"a","b","c"});
        //(2)实例化的时候指定泛型:--》推荐
        Demo02<String> d2 = new Demo02<>(); //指定泛型为String,使用到泛型的所有地方限制只能使用String类型
        d2.sex = "男";
        d2.a("abc");
      	d1.a(18); //报错,Integer类型不能放到泛型为String的集合中
        d2.b(new String[] {"a","b","c"});
    }
}
【2】泛型继承情况:
- 
父类指定泛型: 如果父类指定泛型,子类就不需要再指定泛型了,可以直接使用 
public class SubDemo02Test {
    public static void main(String[] args) {
        //如果父类指定泛型,子类就不需要再指定泛型了,可以直接使用
        SubDemo02 sd2 = new SubDemo02();
        sd2.a(19);
//      	sd2.a("19"); //报错,类的泛型是Integer
    }
}
//父类指定泛型
class SubDemo02 extends Demo02<Integer> {
    public void a() {
    }
}
- 父类不指定泛型:
如果父类不指定泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候指定:
public class SubDemo03Test {
    public static void main(String[] args) {
        SubDemo03<String> sd3 = new SubDemo03<>(); //创建子类对象的时候指定泛型
        sd3.sex = "女";
        sd3.a("abc");
//      	sd3.a(18); //报错,类的泛型是String
    }
}
//父类不指定泛型,子类也会变成泛型类
class SubDemo03<E> extends Demo02<E> {
}
【3】泛型的应用场景

【4】使用泛型的细节⚠️:
- 泛型类可以定义多个参数类型:
 
- 泛型类构造器写法:❌

 
- 不同泛型的引用类型不可以相互赋值:

- 泛型如果不指定,编译器默认泛型为Object类型:

- 泛型类中的静态方法不能使用类的泛型:
- 因为静态方法创建时,泛型A还没有!
 
 
- 不能直接使用 E[]来创建对象:
- 但E[]可作为强制转换的类型
 
 
泛型方法
泛型方法的参数可以是任何引用类型。
泛型方法要求这个方法的泛型参数类型和当前类的泛型无关!!!
- 如果传入的泛型参数是类的泛型,泛型方法就用类的泛型作为泛型方法的泛型
- 如果传入的泛型参数不是类的泛型,泛型方法就用这个泛型作为泛型方法的泛型
package com.collection.demo03;
/**
 * 1.什么是泛型方法?
 * 不是带泛型的方法就是泛型方法
 * 泛型方法要求这个方法的泛型参数类型和当前类的泛型无关!!!
 * 2.T的类型是在调用方法的时候才确定方法的泛型
 * 泛型方法可以是静态方法吗?  可以是静态方法,也可以是普通方法
 * @param <E>
 */
public class Demo04Test {
    public static void main(String[] args) {
        Demo04<String> d = new Demo04<>();
        d.a("abc");
        d.b("abc");
        d.c("abc");
        d.b(19); //泛型从String切换到泛型方法的Integer
        d.b(19.2); //泛型从String切换到泛型方法的Double
        d.c(19); //同上
        d.c(19.2);
    }
}
class Demo04<E> {
    //不是泛型方法
    public void a(E e) { //普通方法使用泛型变量作参数
    }
    //这是泛型方法
    //参数可以是任何引用类型,但和当前类的泛型无关!
    public <T> void b(T t) { //泛型方法使用方法泛型变量作参数,普通泛型方法
    }
    public static <T> void c(T t) { //泛型方法使用方法泛型变量作参数,静态泛型方法
    }
}
泛型参数存在继承关系的情况
不同泛型的引用类型不可以相互赋值
- 如果A是父类,B是A的子类,那么B继承A;泛型 G和G 没有继承关系,而是并列关系,所以相互赋值会报错

通配符
【1】没有通配符的时候:
下面的方法a,相当于方法的重复定义,报错。
package com.collection.demo03;
import java.util.List;
public class Demo06 {
    public void a(List<Object> list) {
        
    }
    public void a(List<String> list) {
        
    }
    public void a(List<Integer> list) {
        
    }
}
【2】引入通配符?:
通配符?是所有泛型的父类
package com.collection.demo03;
import java.util.ArrayList;
import java.util.List;
public class Demo07 {
    public static void main(String[] args) {
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<Integer> list3 = new ArrayList<>();
      	//通配符?是所有泛型的父类
        List<?> list = null;
        list = list1;
        list = list2;
        list = list3;
    }
}
发现:A和B是子类父类的关系,G和G不存在子类父类关系,是并列关系,加入通配符?后,G<?>就变成G和G的父类。
【3】使用通配符
package com.collection.demo03;
import java.util.ArrayList;
import java.util.List;
public class Demo06 {
      public static void main(String[] args) {
        Demo06 d = new Demo06();
        d.a(new ArrayList<Integer>());
        d.a(new ArrayList<String>());
        d.a(new ArrayList<Object>());
        
        //遍历集合元素
        //1、准备一个ArrayList泛型集合
        //2、把数据添加到集合
        //3、调用方法a把集合中的元素输出
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        d.a(list);
    }
}
class Demo06Test {
//    public void a(List<Object> list) {
//
//    }
//    public void a(List<String> list) {
//
//    }
//    public void a(List<Integer> list) {
//
//    }
  
    public void a(List<?> list) {
        //内部遍历的时候用Object,不用通配符?因为需要指定变量类型,通配符不行,Object类是祖宗类等价于通配符
        for (Object a : list)
            System.out.println(a);
    }
}
【4】泛型通配符?在Api文档中大量使用通配符:

使用通配符的细节
package com.collection.demo03;
import java.util.ArrayList;
import java.util.List;
public class Demo06 {
    public static void main(String[] args) {
        Demo06 d = new Demo06();
        d.a(new ArrayList<Integer>());
        d.a(new ArrayList<String>());
        d.a(new ArrayList<Object>());
    }
}
class Demo06Test {
//    public void a(List<Object> list) {
//
//    }
//    public void a(List<String> list) {
//
//    }
//    public void a(List<Integer> list) {
//
//    }
  
  	//遍历集合,泛型为通配符?
    public void a(List<?> list) {
        ////内部遍历的时候用Object,不用通配符?因为需要指定变量类型,通配符不行,Object类是祖宗类等价于通配符
        //1、遍历集合
        for (Object a : list)
            System.out.println(a);
        //2.数据写入操作
//        list.add("abc"); //不能随意的添加数据,因为这时泛型还不知道
        list.add(null);
        
        //3.数据的读取操作
        Object o = list.get(0); //可以读数据,Object类型是集合中的元素的祖宗类
    }
}
泛型受限
- 泛型的上限,List<? extends Person>是List的父类,是List<Person的子类>的父类 
- 泛型的下限,List<? super Person>是List的父类,是List<Person的父类>的父类 
package com.collection.demo04;
import java.util.ArrayList;
import java.util.List;
/*
		继承关系:Person类是父类,Student类是子类,Student类继承Person类
*/
public class Test {
    public static void main(String[] args) {
        //a,b,c这三个集合是并列关系
        List<Object> a = new ArrayList<>();
        List<Person> b = new ArrayList<>();
        List<Student> c = new ArrayList<>();
        /*
        使用泛型受限:泛型的上限
        List<? extends Person>是List<Person>的父类,是List<Person的子类>的父类
         */
        List<? extends Person> list1 = null;
//        list1 = a; //Object类是Peron类的父类
        list1 = b;
        list1 = c;
        /*
        使用泛型受限:泛型的下限
        List<? super Person>是List<Person>的父类,是List<Person的父类>的父类
         */
        List<? super Person> list2 = null;
        list2 = a;
        list2 = b;
//        list2 = c; //Student类是Peron类的子类
    }
}
LinkedList实现类
LinkedList特点:可重复,有序
LinkedList实现类的常用方法
addFirst()、addLast()、offer()、offerFirst()、offerLast()、poll()、pollFirst()、pollLast()、removeFirst()、removeLast()、element()、getFirst()、getLast()、indexOf()、lastIndexOf()、peek()、peekFirst()、peekLast()
package com.collection.demo05;
import java.util.Iterator;
import java.util.LinkedList;
public class Demo01 {
    public static void main(String[] args) {
        /*
        LinkedList常用方法:
        增加:addFirst(E e),addLast(E e),
        offer(E e),offerFirst(E e),offerLast(E e)
        删除:poll(),pollFirst(),pollLast() --> JDK1.6以后出的新方法,提高了代码的健壮性
        removeFirst(),removeLast()
        修改:
        查看:element(),getFirst(),getLast(),
        indexOf(Object o),lastIndexOf(Object o)
        peek(),peekFirst(),peekLast()
        判断:
         */
        //创建一个LinkedList集合对象:
        LinkedList<String> list = new LinkedList<>();
        list.add("aaaaa"); //在集合已满时,add()方法会报IllegalStateException
        list.add("bbbbb");
        list.add("ccccc");
        list.add("ddddd");
        list.add("bbbbb");
        list.add("eeeee");
        System.out.println(list); //LinkedList可以增加重复数据
        list.addFirst("pp"); //在集合头部添加元素
        list.addLast("bb");
        System.out.println(list);
        list.offer("kj"); //添加元素在尾部,在集合已满时,offer()方法只会返回false,表示添加失败
        System.out.println(list);
        list.offerFirst("kk");
        list.offerLast("oc");
        System.out.println(list);
        System.out.println(list.poll()); //删除头部元素并将元素返回
        System.out.println(list.pollFirst());
        System.out.println(list.pollLast());
        System.out.println(list.removeFirst()); //删除头部元素并将元素返回
        System.out.println(list.removeLast());
        System.out.println(list);
//        list.clear(); //清空集合
//        System.out.println(list);
        System.out.println(list.pollFirst()); //list为空时,在删除元素失败时会返回空null,不会报错!
        System.out.println(list.removeFirst()); //list为空时,在删除元素失败时会报NoSuchElementException
        //集合遍历
        System.out.println("-------");
        //普通for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //增强for循环
        for (String s :list) {
            System.out.println(s);
        }
        //迭代器
//        Iterator<String> iterator = list.iterator();
//        while (iterator.hasNext()) {
//            System.out.println(iterator.next());
//        }
        //下面这种方法好,节省内存,while转for循环
        for (Iterator<String> iterator = list.iterator();iterator.hasNext();) {
            System.out.println(iterator.next());
        }
    }
}
LinkedList简要底层原理

模拟LinkedList源码
debug验证数据添加成功:
 
package com.collection.demo05;
class MyLinkedListTest {
    public static void main(String[] args) {
        //创建一个MyLinkedList集合对象
        MyLinkedList ml = new MyLinkedList();
        ml.add("aa");
        ml.add("bb");
        ml.add("cc");
        System.out.println(ml.getSize());
        System.out.println(ml.get(2));
    }
}
public class MyLinkedList {
    //链中一定有一个首节点:
    Node first;
    //链中一定有一个尾节点:
    Node last;
    //计数器
    int count = 0;
    //提供一个空构造器
    public MyLinkedList() {
    }
    //添加元素的方法
    public void add(Object o) {
        if (first == null) { //证明添加的元素是第一个元素
            //将添加的元素封装为一个Note对象
            Node n = new Node();
            n.setPre(null);
            n.setObj(o);
            n.setNext(null);
            //当前链中第一个节点变为n
            first = n;
            //当前链中最后一个节点变为n
            last = n;
        } else { //证明已经不是添加链中第一个节点了
            //将添加的元素封装为一个Note对象
            Node n = new Node();
            n.setPre(last); //n的上一个节点一定是当前链中的最后一个元素last
            n.setObj(o);
            n.setNext(null);
            //当前链中的最后一个节点的下一个元素地址 要指向n
            last.setNext(n);
            //把最后一个节点变为n
            last = n;
        }
        //链中元素数量加1
        count++;
    }
    //得到集合中元素的数量
    public int getSize() {
        return count;
    }
    //通过下标得到元素
    public Object get(int index) {
        Node n = first; //获取链表的头元素
        //遍历链表,从头节点开始直到index位置
        for (int i = 0; i < index; i++) {
            n = n.getNext();
        }
        return n.getObj();
    }
}
package com.collection.demo05;
public class Node {
//三个属性:
    //上一个元素的地址
    private Node pre;
    //存入元素数据
    private Object obj;
    //下一个元素的地址
    private Node next;
    public Node getPre() {
        return pre;
    }
    public void setPre(Node pre) {
        this.pre = pre;
    }
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }
    public Node getNext() {
        return next;
    }
    public void setNext(Node next) {
        this.next = next;
    }
    @Override
    public String toString() {
        return "Node{" +
                "pre=" + pre +
                ", obj=" + obj +
                ", next=" + next +
                '}';
    }
}
LinkedList源码分析
【1】JDK1.7和JDK1.8的LinkedList的源码是一样的。
【2】源码:
public class LinkedList<E> { //E是一个泛型,具体的类型要在实例化的时候才会确定
	transient int size = 0; //集合中元素的数量,不参与序列化
	//Node的静态内部类
	private static class Node<E> {
        E item; //当前元素
        Node<E> next; //指向下一个元素地址
        Node<E> prev; //指向上一个元素地址
        Node(Node<E> prev, E element, Node<E> next) { //有参构造
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
	
	
	transient Node<E> first; //链表的首节点
	transient Node<E> last; //链表的尾节点
  
	//空构造器
	public LinkedList() {
    }
	//添加元素操作
	public boolean add(E e) {
        linkLast(e);
        return true;
    }
	void linkLast(E e) { //e添加的元素
        final Node<E> l = last; //将链表中last节点给l,如果是第一个元素的话l为null
        //将元素封装为一个Node具体的对象
		final Node<E> newNode = new Node<>(l, e, null);
		//将链表的last节点指向新创建的对象
        last = newNode;
        if (l == null) //如果添加的是第一个节点
            first = newNode; //将链表的first节点指向为新节点
        else //如果添加的不是第一个节点
            l.next = newNode; //将l的下一个指向为新节点
        size++; //集合中元素数量+1
        modCount++;
    }
	//获取集合中元素数量
	public int size() {
        return size;
    }
	//通过索引得到元素
	public E get(int index) {
        checkElementIndex(index); //健壮性考虑
        return node(index).item;
    }
	
	Node<E> node(int index) {
        // 如果index在链表的前半段,那么从前往后找
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else { //如果index在链表的后半段,那么从后往前找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
}
面试题:
【1】面试题对应的关系:

【2】hasNext(),Next()的具体实现:

【3】增强for循环的底层也是通过迭代器实现


ListIterator迭代器
package com.collection.demo05;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo02 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");
        //在"cc"之后加一个字符串"kk"
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            if ("cc".equals(it.next())) {
                list.add("kk");
            }
        }
    }
}
发现报错:并发修改异常

出错原因:迭代器和list同时对集合进行操作

解决办法:事情让一个“人”做--》引入新的迭代器:Listiterator
迭代和添加操作都是靠ListIterator来完成。
package com.collection.demo05;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class Demo02 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");
        //在"cc"之后加一个字符串"kk"
//        Iterator<String> it = list.iterator();
//        while (it.hasNext()) {
//            if ("cc".equals(it.next())) {
//                list.add("kk");
//            }
//        }
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()) {
            if ("cc".equals(it.next())) {
                it.add("kk");
            }
        }
        System.out.println(list);
        System.out.println(it.hasNext()); //false,此时it已经到数组最后了
        System.out.println(it.hasPrevious()); //true,it前面有元素
        //逆向遍历
        while (it.hasPrevious()) {
            System.out.println(it.previous());
        }
        System.out.println(it.hasNext()); //true,此时it已经到数组开头
        System.out.println(it.hasPrevious()); //false,此时it已经到数组开头
        System.out.println(list);
    }
}
Set接口
特点:唯一不重复,无序
HashSet实现类
【1】放入Integer类型数据
package com.collection.demo06;
import java.util.HashSet;
public class Demo01 {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<Integer> hs = new HashSet<>();
        hs.add(98);
        System.out.println(hs.add(23)); //true
        hs.add(0);
        hs.add(12);
        System.out.println(hs.add(23)); //false,这个23没有放入到集合中,因为集合中已有23了
        System.out.println(hs.size()); //唯一
        System.out.println(hs); //无序
    }
}
【2】放入String类型数据
package com.collection.demo06;
import java.util.HashSet;
public class Demo02 {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<String> hs = new HashSet<>();
        hs.add("APPLE");
        hs.add("HUAWEI");
        hs.add("OPPO");
        hs.add("Vivo");
        hs.add("HUAWEI");
        hs.add("Xiaomi");
        System.out.println(hs.size()); //唯一
        System.out.println(hs); //无序
    }
}
【3】放入自定义的引用数据类型 数据
下面自定义的类型不满足唯一,无序的特点。为什么会这样?
解决方案:HashSet集合的泛型必须重写两个方法,HashCode()和equals()
程序入口:
package com.collection.demo06;
import java.util.HashSet;
public class TestStudent {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<Student> hs = new HashSet<>(); //泛型是自定义类型
        hs.add(new Student("迪丽热巴", 30));
        hs.add(new Student("古力娜扎", 28));
        hs.add(new Student("马尔扎哈", 15));
        hs.add(new Student("迪丽热巴", 30));
        hs.add(new Student("雷大善人", 50));
        System.out.println(hs.size()); //4,唯一
        System.out.println(hs); //无序
    }
}
Student类
package com.iterable.demo06;
import java.util.Objects;
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 +
                '}';
    }
  	//HashSet集合的泛型必须要重写两个方法,它们是HashCode()和equals()
    @Override
    public boolean equals(Object o) {
        if (this == o) 
          return true;
        if (o == null || getClass() != o.getClass()) 
          return false;
        Student student = (Student)o;
        return age == student.age && name.equals(student.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
【4】HashSet简要原理图

【5】疑问:
- 数组的长度是多少?16
- 数组的类型是什么?
- HashCode(),equals()这两个方法真的调用了吗?验证
- 底层的表达式是什么?
- 同一个位置的数据是向前放还是向后放?
- 放入数组中的数据,是直接放的吗?是否封装为对象了?
LinkedHashSet的使用
LinkedHashSet的特点:唯一不重复,有序
按照输入顺序进行输出。
其实就是在HashSet的基础上,多了一个总的链表,这个总链表将放入的元素串在一起,方便有序遍历。

package com.collection.demo06;
import java.util.LinkedHashSet;
public class Demo03 {
    public static void main(String[] args) {
        //创建一个LinkedHashSet集合
        LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
        lhs.add(98);
        lhs.add(23); //true
        lhs.add(0);
        lhs.add(12);
        lhs.add(23); //false,这个23没有放入到集合中
        System.out.println(lhs.size()); //唯一
        System.out.println(lhs); //有序
    }
}
比较器的使用
【1】以int类型为例:
比较的思路:将比较的数据做差,然后返回一个int类型的数据,将这个int类型的数据,按照 =0,>0,<0规则
package com.collection.demo07;
public class Demo01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println(a - b); //=0 相等, >0 a大, <0 a小
    }
}
【2】比较String类型的数据
String类实现了Comparable接口,这个接口中有一个抽象方法compareTo,String类中已经重写了这个方法。
使用compareTo()方法比较包装类
package com.collection.demo07;
public class Demo01 {
    public static void main(String[] args) {
        //0 相等, 1 a大, -1 a小
        String a = "A";
        String b = "B";
        System.out.println(a.compareTo(b));
    }
}
【3】比较Double类型的数据:
使用compareTo()方法比较包装类(Double)
package com.collection.demo07;
public class Demo01 {
    public static void main(String[] args) {
        double e = 9.6;
        double f = 9.3;
//        System.out.println((int)(e - f)); //0,丢失精度
      	//0 相等, 1 a大, -1 a小
        System.out.println(((Double)e).compareTo((Double)f)); //compareTo方法比较包装类
    }
}
【3】比较自定义的数据类型:
<1> 内部比较器 Comparable
程序入口:
package com.collection.demo07;
public class Demo02 {
    public static void main(String[] args) {
        //比较两个学生
        Student s1 = new Student("a迪丽热巴", 27, 170.5);
        Student s2 = new Student("b古力娜扎", 22, 168.4);
        System.out.println(s1.compareTo(s2));
    }
}
Student类:
package com.collection.demo07;
public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private double height;
  
  	public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    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;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
	  //不能一次返回多个值
    @Override
    public int compareTo(Student o) {
        //按照年龄进行比较
//        return this.getAge() - o.getAge();
        //按照身高进行比较
//        return (((Double)this.getHeight())).compareTo(((Double)o.getHeight()));
        //按照名字进行比较
        return this.getName().compareTo(o.getName());
    }
}
<2> 外部比较器Comparator
程序入口:
package com.collection.demo07;
import java.util.Comparator;
public class Demo03 {
    public static void main(String[] args) {
        //创建两个自定义的对象
      	//比较两个学生
        Student1 s1 = new Student1("a迪丽热巴", 27, 170.5);
        Student1 s2 = new Student1("b古力娜扎", 22, 168.4);
      
        //获取外部比较器
//        Comparator bj1 = new BiJiao01();
//        System.out.println(bj1.compare(s1, s2));
        Comparator bj1 = new BiJiao02();
        System.out.println(bj1.compare(s1, s2));
//        Comparator bj1 = new BiJiao03();
//        System.out.println(bj1.compare(s1, s2));
    }
}
Student1类:
package com.collection.demo07;
import java.util.Comparator;
public class Student1 {
    private String name;
    private int age;
    private double height;
    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;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student1(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}
class BiJiao01 implements Comparator<Student1> {
    @Override
    public int compare(Student1 o1, Student1 o2) {
        //按照年龄进行比较
        return o1.getAge() - o2.getAge();
    }
}
class BiJiao02 implements Comparator<Student1> {
    @Override
    public int compare(Student1 o1, Student1 o2) {
        //按照名字进行比较
        return o1.getName().compareTo(o2.getName());
    }
}
class BiJiao03 implements Comparator<Student1> {
    @Override
    public int compare(Student1 o1, Student1 o2) {
        //在年龄相同的情况下,比较身高,年龄不同比较年龄
        if ((o1.getAge() - o2.getAge()) == 0) {
            return ((Double)(o1.getHeight())).compareTo((Double)(o2.getHeight()));
        } else {
            return o1.getAge() - o2.getAge();
        }
    }
}
【5】外部比较器和内部比较器哪个更好?
外部比较器更好,因为它更灵活,它采用了多态,它的扩展性更好
TreeSet实现类
TreeSet实现类的使用
TreeSet集合的特点:唯一不重复,有序,按照升序排列元素,但是没有按照输入顺序进行输出。
【1】存入Integer类型的数据
Integer底层实现了内部比较器Comparable
程序入口:
package com.collection.demo08;
import java.util.TreeSet;
public class Demo01 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(12);
        ts.add(3);
        ts.add(2);
        ts.add(5);
        ts.add(12);
        ts.add(45);
        System.out.println(ts.size()); //唯一
        System.out.println(ts); //有序,升序
    }
}
【2】原理:
TreeSet的底层是二叉树,二叉树是数据结构中的一个逻辑结构。
规则:小放左,大放右

【3】放入String类型的数据
String底层实现了内部比较器Comparable
package com.collection.demo08;
import java.util.TreeSet;
public class Demo02 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        TreeSet<String> ts = new TreeSet<>();
        ts.add("alili");
        ts.add("blili");
        ts.add("elili");
        ts.add("dlili");
        ts.add("alili");
        ts.add("flili");
        System.out.println(ts.size()); //唯一
        System.out.println(ts); //有序,升序
    }
}
【4】放入自定义引用类型的数据
<1> 使用内部比较器:
需要自己重写compareTo()
程序入口:
package com.collection.demo08;
import java.util.TreeSet;
public class Demo03 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        TreeSet<Student> ts = new TreeSet<>();
        ts.add(new Student("dlili", 8));
        ts.add(new Student("clili", 10));
        ts.add(new Student("alili", 3));
        ts.add(new Student("blili", 5));
        ts.add(new Student("elili", 8));
        ts.add(new Student("alili", 1));
        System.out.println(ts.size());
        System.out.println(ts);
    }
}
Student类:
package com.collection.demo08;
public class Student implements Comparable<Student> {
    private String name;
    private int 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;
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        return this.getAge() - o.getAge(); //比较年龄
//      	return this.getName().compareTo(o.getName()); //比较名字
    }
}
<2> 使用外部比较器Comparator
实际开发中使用外部比较器多,因为它的扩展性好,它使用了多态。
package com.collection.demo08;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo03 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        //想使用外部比较器,必须自己指定
        Comparator<Student> bj = new BiJiao();
        TreeSet<Student> ts = new TreeSet<>(bj); //一旦指定外部比较器,就会按照外部比较器进行比较
        ts.add(new Student("dlili", 8));
        ts.add(new Student("clili", 10));
        ts.add(new Student("alili", 3));
        ts.add(new Student("blili", 5));
        ts.add(new Student("elili", 8));
        ts.add(new Student("alili", 1));
        System.out.println(ts.size());
        System.out.println(ts);
    }
}
Student类:
package com.collection.demo08;
import java.util.Comparator;
public class Student {
    private String name;
    private int 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;
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
class BiJiao implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());
    }
}
换一种写法,匿名类:
package com.collection.demo08;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo03 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        //想使用外部比较器,必须自己指定
        Comparator<Student> bj = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getName().compareTo(o2.getName());
            }
        };
        TreeSet<Student> ts = new TreeSet<>(bj); //一旦指定外部比较器,就会按照外部比较器进行比较
        ts.add(new Student("dlili", 8));
        ts.add(new Student("clili", 10));
        ts.add(new Student("alili", 3));
        ts.add(new Student("blili", 5));
        ts.add(new Student("elili", 8));
        ts.add(new Student("alili", 1));
        System.out.println(ts.size());
        System.out.println(ts);
    }
}
【5】TreeSet底层的二叉树遍历是按照升序的结果出现的,这个升序是靠中序遍历得到的。

Map接口
特点:唯一,无序
Map接口常用方法
put()、clear()、remove()、entrySet()、get()、keySet()、size()、values()、containsKey()、containsValue()、equals()、isEmpty()
package com.collection.demo09;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo01 {
    public static void main(String[] args) {
        /*
        Map接口常用方法
        增加:put(K key, V value)
        删除:clear() remove(Object key)
        修改:
        查看:entrySet() get(Object key) keySet() size() values()
        判断:containsKey(Object key) containsValue(Object value)
             equals(Object o) isEmpty()
         */
        //创建一个Map集合,创建实现类对象HashMap
        Map<String, Integer> map = new HashMap<>();
        map.put("迪丽热巴", 1234323124);
        map.put("古力娜扎", 432435232);
        map.put("马尔扎哈", 2123432432);
        map.put("迪丽热巴", 342432522);
        map.put("勇敢牛牛", 344232522);
//        map.clear(); //清空
//        map.remove("古力娜扎"); //移除
        System.out.println(map.size()); //唯一
        System.out.println(map); //无序 {key1=value1,key2=value2}
        System.out.println(map.containsKey("勇敢牛牛"));
        System.out.println(map.containsValue(432435232));
        Map<String, Integer> map2 = new HashMap<>();
        map2.put("迪丽热巴", 1234323124);
        map2.put("古力娜扎", 432435232);
        map2.put("马尔扎哈", 2123432432);
        map2.put("迪丽热巴", 342432522);
        map2.put("勇敢牛牛", 344232522);
        System.out.println(map2.size()); //唯一
        System.out.println(map2); //无序
        System.out.println(map == map2); //比较两个集合的地址是否相等
        System.out.println(map.equals(map2)); //比较两个集合的内容是否相等
        System.out.println("判断集合是否为空:" + map.isEmpty());
        System.out.println(map.get("勇敢牛牛")); //获取key对应的value
        //keySet()对集合中的key遍历查看
        Set<String> set = map.keySet();
        for (String s : set) {
            System.out.println(s);
        }
        //values()对集合中的value遍历查看
        Collection<Integer> values = map.values();
        for (Integer i : values) {
            System.out.println(i);
        }
        //用keySet()、get(Object key)对集合中的value进行遍历查看
        Set<String> set2 = map.keySet();
        for (String s : set2) {
            System.out.println(map.get(s));
        }
        //entrySet()
        Set<Map.Entry<String, Integer>> entries = map.entrySet(); //泛型是Map.Entry<String, Integer>
        for (Map.Entry<String, Integer> e : entries) {
            System.out.println(e.getKey()+"---"+e.getValue()); //从Map集合中获取key和value
        }
    }
}
Hashtable,LinkedHashMap的使用
Hashtable是JDK1.0,线程安全(key不能放null),效率低,唯一,无序
HashMap是JDK1.2,线程不安全(key能放null),效率高,唯一,无序
LinkedHashMap,线程不安全(key能放null),效率高,唯一,有序且是升序
package com.collection.demo09;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
public class Demo02 {
    public static void main(String[] args) {
        //创建一个Hashtable集合
        Map<String, Integer> map = new Hashtable<>();
        map.put(null, 3123212); //报错,Hashtable的key不能放入null值
        map.put("a迪丽热巴", 1234323124);
        map.put("b古力娜扎", 432435232);
        map.put("c马尔扎哈", 2123432432);
        map.put("a迪丽热巴", 342432522);
        map.put("d勇敢牛牛", 344232522);
        System.out.println(map.size()); //唯一
        System.out.println(map); //无序
        //创建一个HashMap集合
        Map<String, Integer> map1 = new HashMap<>();
        map1.put(null, 3123212); //HashMap的key可以放入null值
        map1.put(null, 32131544);
        map1.put("a迪丽热巴", 1234323124);
        map1.put("b古力娜扎", 432435232);
        map1.put("c马尔扎哈", 2123432432);
        map1.put("a迪丽热巴", 342432522);
        map1.put("d勇敢牛牛", 344232522);
        System.out.println(map1.size()); //唯一
        System.out.println(map1); //无序
        //创建一个LinkedHashMap集合
        Map<String, Integer> map2 = new LinkedHashMap<>();
        map2.put(null, 3123212); //LinkedHashMap的key可以放入null值
        map2.put(null, 32131544);
        map2.put("a迪丽热巴", 1234323124);
        map2.put("b古力娜扎", 432435232);
        map2.put("c马尔扎哈", 2123432432);
        map2.put("a迪丽热巴", 342432522);
        map2.put("d勇敢牛牛", 344232522);
        System.out.println(map2.size()); //唯一
        System.out.println(map2); //有序,升序
    }
}
TreeMap
特点:唯一,有序且是升序,线程安全(key不能放null),效率低
【1】Key的类型为String类型
package com.collection.demo09;
import java.util.Map;
import java.util.TreeMap;
public class Demo03 {
    public static void main(String[] args) {
        //创建一个TreeMap集合
        Map<String, Integer> map = new TreeMap<>();
      	map.put(null, 1234346124); //报错,TreeMap的key不能放入null值
        map.put("a迪丽热巴", 1234323124);
        map.put("b古力娜扎", 432435232);
        map.put("c马尔扎哈", 2123432432);
        map.put("a迪丽热巴", 342432522);
        map.put("d勇敢牛牛", 344232522);
        System.out.println(map.size()); //唯一
        System.out.println(map); //有序,升序
    }
}
【2】Key的类型是一个自定义的引用数据类型
<1> 使用内部比较器:
程序入口:
package com.collection.demo09;
import java.util.Map;
import java.util.TreeMap;
public class Demo04 {
    public static void main(String[] args) {
        Map<Student, Integer> map = new TreeMap<>();
        map.put(new Student("a迪丽热巴", 18, 175.9), 1001);
        map.put(new Student("b古力娜扎", 20, 177.0), 1002);
        map.put(new Student("c马尔扎哈", 30, 170.0), 1003);
        map.put(new Student("a迪丽热巴", 18, 165.6), 1004);
        map.put(new Student("d勇敢牛牛", 8, 185.5), 1005);
        System.out.println(map.size());
        System.out.println(map);
    }
}
内部比较器Comparable
package com.collection.demo09;
public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private double height;
    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;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        //按照年龄排序
        return this.getAge() - o.getAge();
        //按照名字排序
//        return this.getName().compareTo(o.getName());
        //按照身高排序
//        return ((Double)(this.getHeight())).compareTo((Double)(o.getHeight()));
    }
}
<2> 使用外部比较器:
程序入口:
package com.collection.demo09;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Demo04 {
    public static void main(String[] args) {
//        Map<Student, Integer> map = new TreeMap<>();
//        map.put(new Student("a迪丽热巴", 18, 175.9), 1001);
//        map.put(new Student("b古力娜扎", 20, 177.0), 1002);
//        map.put(new Student("c马尔扎哈", 30, 170.0), 1003);
//        map.put(new Student("a迪丽热巴", 18, 165.6), 1004);
//        map.put(new Student("d勇敢牛牛", 8, 185.5), 1005);
//        System.out.println(map.size());
//        System.out.println(map);
      	//使用外部比较器
        Comparator<Student> bj = new BiJiao1();
        Map<Student, Integer> map = new TreeMap<>(bj); //一旦指定外部比较器,就会按照外部比较器进行比较
        map.put(new Student("a迪丽热巴", 18, 175.9), 1001);
        map.put(new Student("b古力娜扎", 20, 177.0), 1002);
        map.put(new Student("c马尔扎哈", 30, 170.0), 1003);
        map.put(new Student("a迪丽热巴", 18, 165.6), 1004);
        map.put(new Student("d勇敢牛牛", 8, 185.5), 1005);
        System.out.println(map.size());
        System.out.println(map);
    }
}
外部类比较器Comparator
package com.collection.demo09;
import java.util.Comparator;
public class Student {
    private String name;
    private int age;
    private double height;
    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;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}
//外部类比较器
class BiJiao1 implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //按照年龄排序
        return o1.getAge() - o2.getAge();
    }
}
class BiJiao2 implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //按照名字排序
        return o1.getName().compareTo(o2.getName());
    }
}
class BiJiao3 implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //按照身高排序
        return ((Double)(o1.getHeight())).compareTo((Double)(o2.getHeight()));
    }
}
HashMap底层原理(难点重点)
HashMap底层原理:

深入理解HashMap底层
深入理解HashMap源码,以JDK1.7为例。
- HashMap的K,V值,在创建对象的时候才确定。K:String V:Integer
- HashMap的父类AbstractMap已经实现了Map接口,但是源码中又单独实现了Map接口,这是多余的操作。
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    //重要属性:
    static final int DEFAULT_INITIAL_CAPACITY = 16; //定义了一个常量16,是一会要赋给数组的长度
    static final int MAXIMUM_CAPACITY = 1 << 30; //定义了一个很大的数
    static final float DEFAULT_LOAD_FACTOR = 0.75f; //定义了一个值0.75,是负载因子
    transient Entry<K,V>[] table; //底层主数组,类型是Entry<K,V>[]
    transient int size; //元素的数量
    int threshold; //定义了一个变量,没赋值默认为0,-->这个变量是用来表示数组扩容的阈值,边界值
    final float loadFactor; //这个变量用来接收 负载因子
HashMap构造器:
//空构造器:
public HashMap() {
//this(16, 0.75)
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
//有参构造器:
public HashMap(int initialCapacity, float loadFactor) {
   //capacity 一定是2的整数倍 2^n
   int capacity = 1;
   while (capacity < initialCapacity)
       capacity <<= 1;
   //确定了负载因子:0.75
   this.loadFactor = loadFactor;
   //threshold = capacity * loadFactor = 16*0.75=12
   //threshold=12 --》数组扩容的阈值,边界值
   threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
   //创建主数组,主数组的长度定义为16
   table = new Entry[capacity];
}
存数据的方法:
//存储数据的方法:
public V put(K key, V value) { //K:String	V:Integer
    //对空值进行判断-->允许key的值为null
    if (key == null)
        return putForNullKey(value);
    //获取哈希码HashCode
    int hash = hash(key);
    //得到元素在数组中的位置
    int i = indexFor(hash, table.length);
    //如果放入的数组位置上没有元素的话,那么直接添加就行了,不用走这个for循环
    
    //e != null满足的话,就证明这个位置上已经有东西了!
    for (Entry<K,V> e = table[i]; e!= null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            //获取旧的value
            V oldValue = e.value;
            e.value = value; //新的value替换旧的value,只换value,不换key
            e.recordAccess(this);
            return oldValue; //将oldValue返回
        }
    }
}
扩容数组:
HashMap经典面试题:


HashSet底层原理
HashSet底层就是依靠HashMap来实现的。
HashSet源码重要属性:
public class HashSet<E> {
    //重要属性:
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    //构造器:
    public HashSet() {
        map = new HashMap<>(); //HashSet底层就是利用HashMap来实现的
    }
    
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
}
TreeMap底层原理
【1】简要原理图:

【2】源码:
public class TreeMap<K,V> {
    //重要属性:
    //外部比较器
    private final Comparator<? super K> comparator;
    //二叉树的根
    private transient Entry<K,V> root = null;
    //集合中元素的数量
    private transient int size = 0;
    //空构造器:
    public TreeMap() {
        comparator = null; //如果使用空构造器,那么底层就不使用外部比较器
    }
    //有参构造器:
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator; //如果使用有参构造器,那么就相当于指定了外部比较器
    }
}
//节点组成部分
static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left = null;
        Entry<K,V> right = null;
        Entry<K,V> parent;
        boolean color = BLACK;
}



TreeSet底层原理
TreeSet底层就是依靠TreeMap来实现的。
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable {
    //重要属性:
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    
    //在调用空构造器的时候,底层创建了一个TreeMap
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
}
Collections工具类
Collections类中的属性和方法都是被static修饰的,因此我们可以用 类名. xx 直接调用它们。
更多方法请查看API帮助文档!
常用方法:
addAll()、sort()、binarySearch()、copy()、fill()
package com.collection.demo10;
import java.util.ArrayList;
import java.util.Collections;
public class Demo01 {
    public static void main(String[] args) {
        //Collections不能创建对象,因为构造器私有化了
//        Collections cols = new Collections();
        //里面的属性和方法都是被static修饰,因此我们可以用 类名.xx 直接调用它们
        //常用方法:
        //addAll()
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        Collections.addAll(list, "dd", "ee", "ff");
        Collections.addAll(list, new String[] {"gg", "oo", "jj"});
        System.out.println(list);
        //sort()对集合进行排序
        //binarySearch(),必须在有序的集合中查找
        Collections.sort(list); //升序
        System.out.println(Collections.binarySearch(list, "cc")); //2,搜索结果是集合索引为2的元素
        //copy():替换集合元素到另一个集合(覆盖)
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "bb", "ee", "ff");
        Collections.copy(list, list1); //将list1的内容替换到list中
        System.out.println(list);
        System.out.println(list1);
        //fill:填充
        Collections.fill(list1, "yyds");
        System.out.println(list1);
    }
}
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号