一.泛型(JDK1.5以后出现的机制)
1.泛型由来
为什么会有泛型呢? 通过案例引入 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
演示:
import java.util.ArrayList;
import java.util.Iterator;
/*
    在此之前的集合,我们使用的时候,可以传入不同的数据类型的元素。
    但是,实际开发中,一个集合只能够存储一种数据类型,为了就是将来获取元素处理的时候,处理方式能够统一
    想一想之前也学习过一种容器,这种容器在定义的时候,就明确了元素的数据类型(数组)
    现在集合想要模仿使用定义数组时的特点,可以明确一个集合元素的数据类型。
    java为了实现这样的功能,提供了一个技术给我们使用:泛型
    语句定义格式:<引用数据类型>
    泛型的好处:
        1、消除了程序中大部分的黄色警告
        2、在获取元素的时候,不需要做向下转型
        3、限制了集合存储的数据类型,将来处理的方式统一
 */
public class ArrayListDemo1 {
    public static void main(String[] args) {
//        //创建集合对象
//        ArrayList list = new ArrayList();
//
//        //创建元素对象并添加到集合中
//        list.add("java");
//        list.add("hadoop");
//        list.add("hive");
//        list.add("java");
//        list.add("hbase");
        list.add(12);
//
//        //迭代器遍历
//        Iterator iterator = list.iterator();
//        while (iterator.hasNext()) {
//            String s = (String) iterator.next();
//            System.out.println(s + "--" + s.length());
//        }
        //加入了泛型创建集合对象
        //  ArrayList<E>
        ArrayList<String> list2 = new ArrayList<>(); // 后面的尖括号泛型中的数据类型,根据前面定义时的泛型自动类型推断
        // String[] arr;
        //创建元素对象并添加到集合中
        list2.add("java");
        list2.add("hadoop");
        list2.add("hive");
        list2.add("java");
        list2.add("hbase");
//        list2.add(12);
        //迭代器遍历
        Iterator<String> iterator2 = list2.iterator();
        while (iterator2.hasNext()) {
            String s = iterator2.next();
            System.out.println(s + "--" + s.length());
        }
    }
}
2.泛型应用
泛型类
把泛型定义在类上
格式: public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型
演示:
/*
    开发代码的时候,写的泛型,将来使用的时候,应该传入一个引用数据类型给到开发时的泛型中接收
    将类当作参数一样进行传递
    <>中应该定义一个变量,用于接收将来使用时传入的引用数据类型,有点类似于形式参数
    就要符合标识符的命名规则
    如果泛型中只需要接收一个引用数据类型,定义时,只需要写一个大写的字母
 */
class Demo2<E> {      // Demo2<Integer>
    //E在一个类中是唯一的
    public void fun(E a) {    //Integer
        System.out.println(a);
    }
}
/*
    泛型类:在开发代码的时候,将泛型写在类上
 */
public class FanXingDemo2 {
    public static void main(String[] args) {
        Demo2<Integer> demo2 = new Demo2<>();
        demo2.fun(11);
    }
}
泛型方法
把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 ...)
演示:
class Demo3 {
    //泛型方法,将来调用时可以传入任意的数据类型,与类上面的泛型无关
    public <E> void fun3(E a) {
        System.out.println(a);
    }
}
/*
    泛型方法:将泛型定义在方法上
 */
public class FanXingDemo3 {
    public static void main(String[] args) {
        Demo3 s3 = new Demo3();
        s3.fun3(11);
        s3.fun3('s');
    }
}
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>
import java.util.ArrayList;
import java.util.List;
//如果一个类实现一个接口,这个接口有泛型的话,类定义时,也要有泛型,一般情况下与接口的泛型写法一致
interface Inter<E> {
    void fun4(E a);
}
class InterImtl<E> implements Inter<E> {
    @Override
    public void fun4(E a) {
        System.out.println(a);
    }
}
public class FanXingDemo4 {
    public static void main(String[] args) {
        Inter<String> inter = new InterImtl<>();
        inter.fun4("xuyou");
//        inter.fun4(23);
        List<String> list1 = new ArrayList<>();
        list1.add("12");
        System.out.println(list1);
//        list1.add(12);
    }
}
3.泛型通配符
第一种:
<?> 任意类型,如果没有明确,那么就是Object以及任意的Java类
第二种:
? extends E 向下限定,E及其子类
第三种:
? super E 向上限定,E及其父类
演示:
import java.util.ArrayList;
import java.util.Collection;
/*
    开发程序时,泛型的高级用法:
        泛型通配符<?>
            任意类型,如果没有明确,那么就是Object以及任意的Java类了
        ? extends E
            向下限定,E及其子类
        ? super E
            向上限定,E及其父类
 */
class Animal {
    public void fun() {
    }
}
class Dog extends Animal {
}
class Cat extends Animal {
}
class Demo5<E> {    E: Animal
    public void fun(ArrayList<? super E> e) {
        System.out.println(e);
    }
}
public class FanXingDemo5 {
    public static void main(String[] args) {
        // 向下限定,E及其子类
        ArrayList<? extends Animal> list1 = new ArrayList<Animal>();
        ArrayList<? extends Animal> list2 = new ArrayList<Dog>();
        ArrayList<? extends Animal> list3 = new ArrayList<Cat>();
//        ArrayList<? extends Animal> list4 = new ArrayList<Object>();
        //E: Animal
        // ? super E 向上限定
        Demo5<Animal> d = new Demo5<>();
        //ddAll(Collection<? extends E> c)
        // Collection<? extends E> 表示将来应该传入一个Collection集合对象,并且集合中的元素类型是E本身或者是E的子类类型
        // E: Animal
        ArrayList<Animal> list4 = new ArrayList<>();
        ArrayList<Dog> list5 = new ArrayList<>();
        ArrayList<Cat> list6 = new ArrayList<>();
        ArrayList<Object> list7 = new ArrayList<>();
        
        d.fun(list4);
        d.fun(list7);
//        d.fun(list5);
        
    }
}
4.常用类型:
E:代表元素(Element)类型,通常在集合中使用,如 List<E>。
 K:代表键(Key)的类型,通常在 Map 中使用,如 Map<K, V>。
 V:代表值(Value)的类型,通常在 Map 中使用,如 Map<K, V>。
 T:代表任意类型(Type),通常在方法中使用,如 public <T> T method(T obj)。
 N:代表数字类型,如 Number 类型。
 R:代表返回类型,如 public <T> R method(T obj)
二. 增强for循环
1.概述
(1)是用于遍历Collection集合和数组的
(2)增强for循环存在的目的,主要适用于替代Collection中的迭代器的。
2.格式
for(元素数据类型 变量 : 数组或者Collection集合) {
语句体 (使用变量即可,该变量就是元素)
}
3.注意事项:
增强for的目标要判断是否为null
演示:
import java.util.ArrayList;
import java.util.Objects;
/*
    增强for循环:是用于遍历Collection集合和数组的
    语句定义格式:
        for(元素数据类型 变量名 : Collection集合/数组){
           直接使用变量名;
        }
    增强for循环存在的目的,主要适用于替代Collection中的迭代器的。
 */
public class Demo1 {
    public static void main(String[] args) {
        //遍历数组
//        int [] arr = null;
        int [] arr ={11,22,33,44,55};
        for (int i : arr) {
            System.out.println(i);
        }
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++");
        //遍历集合
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("nihao");
        list1.add("xuyou");
        list1.add("caozei");
        list1.add("11");
        for (String s : list1) {
            System.out.println(s);
        }
    }
}
三.静态导入
1.概述:
(1)格式:import static 包名….类名.方法名;
(2)可以直接导入到方法的级别
2.注意事项
(1)方法必须是静态的
(2)如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀。
四.可变参数
1.概述:指的是方法将来调用时,可以传入若干个指定类型的参数
2.使用场景:定义方法的时候不知道该定义多少个参数时使用
3.注意:
(1)一个方法定义中只能有一个可变参数
(2)如果一个方法有多个参数和可变参数,那么可变参数要最后一个定义
演示:
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/*
     可变参数:指的是方法将来调用时,可以传入若干个指定类型的参数
     注意:
         1、一个方法定义中只能有一个可变参数
         2、可变参数必须在参数列表中的最后一个定义
  */
public class KeBianDemo {
    public static void main(String[] args) {
        //需求1:定义一个方法,求四个int类型值的和
        System.out.println(getSum(1, 2, 3, 4));
        //需求2:定义一个方法,传入姓名以及各科分数,求总成绩
        getSum("小明", 11, 22, 33, 44);
    }
    
    //将来传入若干个int类型的元素值,JVM将其封装到一个数组中,这个数组名叫做arr
    public static int getSum(int... arr) {
        int scoreSum = 0;
        for (int i : arr) {
            scoreSum += i;
        }
        return scoreSum;
    }
    public static void getSum(String name, int... arr) {
        int scoreSum = 0;
        for (int i : arr) {
            scoreSum += i;
        }
        System.out.println(name + " " + scoreSum);
    }
}
五. List集合练习
1.集合的嵌套遍历
import java.util.ArrayList;
/*
    集合的嵌套遍历
        学校(集合),目前有4个班级 1,2,3
        每个班级也是一个集合 ArrayList<Student> class = new ArrayList<>();
 */
class Student {
    String name;
    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 "name:" + name + ",age:" + age;
    }
}
public class ListTest1 {
    public static void main(String[] args) {
        ArrayList<Student> class1 = new ArrayList<>();
        class1.add(new Student("小1", 18));
        class1.add(new Student("小2", 14));
        ArrayList<Student> class2 = new ArrayList<>();
        class1.add(new Student("小3", 16));
        class1.add(new Student("小4", 18));
        ArrayList<Student> class3 = new ArrayList<>();
        class1.add(new Student("小5", 13));
        class1.add(new Student("小6", 17));
        class1.add(new Student("小7", 21));
        ArrayList<ArrayList<Student>> school = new ArrayList<>();
        school.add(class1);
        school.add(class2);
        school.add(class3);
        for (ArrayList<Student> clazz : school) {
            for (Student student : clazz) {
                System.out.println(student);
            }
        }
    }
}
2.获取10个1-20之间的随机数,要求不能重复
import java.util.ArrayList;
import java.util.Random;
//2.获取10个1-20之间的随机数,要求不能重复
public class ListDemo2 {
    public static void main(String[] args) {
        ArrayList<Integer> numberLIst = new ArrayList<>();
        Random random = new Random();
        while (numberLIst.size() < 10) {
            int number = random.nextInt(20)+1;
            if (!numberLIst.contains(number)) {
                numberLIst.add(number);
            }
        }
        System.out.println(numberLIst);
    }
}
3.键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/*
    键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
 */
public class LIstTest3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        ArrayList<Integer> list = new ArrayList<>();
        int number = -1;
        int i = 1;
        while (number!=0) {
            System.out.print("请输入第" + i + "个数据:");
            i++;
            int number1 = sc.nextInt();
            if(number1==0){
                break;
            }
            list.add(number1);
        }
        //方式一:
        //将集合中的第一个元素默认为最大值
        Integer maxNumber = list.get(0);
        for (Integer num : list) {
            if (num>maxNumber){
                maxNumber=num;
            }
        }
        System.out.println("最大值为:"+maxNumber);
        //方式二:利用Collecyions工具类
//        System.out.println("最大值为:"+Collections.max(list));
    }
}
六.Set接口及其子类HashSet
1.概述
Set(接口) :元素无序且唯一 (无序:存储和取出的顺序不一致)
--HashSet : 底层数据结构是哈希表,线程不安全的,效率高
2.HashSet
(1)构造方法:
HashSet() 创建一个空的集合
(2)注意:
(1)HashMap实例具有默认初始容量(16)和负载因子(0.75)
(2)HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法
演示:
import java.util.HashSet;
import java.util.Objects;
/*
    使用HashSet存储学生对象,当学生对象的姓名和年龄一样的时候,表示是同一个学生,应该要进行去重
    结论:
        HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法。
 */
class Student {
    String name;
    int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @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 && Objects.equals(name, student.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class HashSetDemo {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<Student>  studentSet= new HashSet<>();
        //创建学生对象studentSet
        Student student1 = new Student("小明", 12);
        Student student2 = new Student("小花", 14);
        Student student3 = new Student("小二", 18);
        Student student4 = new Student("小李", 22);
        Student student5 = new Student("小明", 12);
        //将元素添加到HashSet集合中
        studentSet.add(student1);
        studentSet.add(student2);
        studentSet.add(student3);
        studentSet.add(student4);
        studentSet.add(student5);
        //遍历集合
        for (Student student : studentSet) {         //此时遍历后的结果并没有去重,原因是底层源码中没有重写equals和hashCode方法,比较的是地址值
            System.out.println(student);
        }
    }
}
HashSet中的add()方法源码分析:
public class HashSet<E> implements Set<E>{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
    //set1.add("pitaya");
    public boolean add(E e) {
        //HashSet中的add方法底层是调用了HashMap中的put方法
        // e -- student1
        // PRESENT -- new Object()
        return map.put(e, PRESENT)==null;
    }
}
public class HashMap<K,V> implements Map<K,V>{
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    public V put(K key, V value) {
        // key -- "pitaya"
        // value -- new Object()
        // 与元素类型中的hashCode方法有关
        return putVal(hash(key), key, value, false, true);
    }
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        Node<K,V>[] tab;
        Node<K,V> p;
        int n, i;
        //第一次初始化一个hashTable出来
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //第一个元素,直接放入到哈希表中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e;
            K k;
            //判断待插入的元素与已经在哈希表中的元素
            //1、哈希值是否一样 元素类.hashCode()方法
            //2、内容值是否一样 元素类.equals()方法
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
}
3. LinkedHashSet类
1.概述:
(1)为HashSet的子类,底层数据结构是哈希表与双向链表
(2)元素有序唯一,由链表保证元素有序,由哈希表保证元素唯一
2.演示:
/*
    Collection
        - List
            -- ArrayList
            -- Vector
            -- LinkedList
        - Set(接口) 元素无序且唯一
            -- HashSet 底层数据结构是哈希表,线程不安全的,效率高
                --子类:LinkedHashSet 底层数据结构是哈希表与双向链表
 */
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //创建LinkedHashSet集合
        LinkedHashSet<Integer> set = new LinkedHashSet<>();
        //添加元素
        set.add(11);
        set.add(2);
        set.add(33);
        set.add(11);
        set.add(23);
        //遍历
        for (Integer i : set) {
            System.out.println(i);
        }
    }
}
4.TreeSet类
1.概述:
是set接口实现的具体子类
2.排序方式:
(1)使用元素的自然顺序Comparable<T>对元素进行排序(自然排序)
(2)根据创建 set 时提供的Comparator进行排序(比较器排序)
如何使用这两种排序具体取决于使用的构造方法
3.TreeSet是如何保证元素的排序和唯一性的:
底层数据结构是红黑树(红黑树是一种自平衡的二叉树)
4.构造方法

5.自然排序:
(1)演示:
import java.util.TreeSet;
/*
    Collection
        - List
            -- ArrayList
            -- Vector
            -- LinkedList
        - Set(接口) 元素无序且唯一
            -- HashSet 底层数据结构是哈希表,线程不安全的,效率高
                --子类:LinkedHashSet 底层数据结构是哈希表与双向链表
            -- TreeSet 底层数据结构是红黑树(可排序的),线程不安全,效率高
    TreeSet中的排序方式有两种:
        1、自然排序  Comparable
        2、比较器排序 comparator
    需求1:使用TreeSet存储字符串对象,并遍历
    通过观察源码发现,因为我们创建TreeSet集合对象的时候,使用的是无参构造方法
    所以底层创建TreeMap的时候也是无参构造方法,comparator是null,不走比较器排序
    走的是自然排序
 */
public class TreeSetDemo1 {
    public static void main(String[] args) {
        //创建TreeSet集合
        TreeSet<Integer> treeSet = new TreeSet<>();
        //添加元素
        treeSet.add(11);
        treeSet.add(22);
        treeSet.add(87);
        treeSet.add(43);
        treeSet.add(98);
        //遍历
        for (Integer i : treeSet) {
            System.out.println(i);        //元素从小到大排序
        }
    }
}(2)实例:创建学生对象,加入到TreeSet集合中,按照年龄从小到大排序输出,要求使用自然排序
演示:
import java.util.TreeSet;
/*
    使用TreeSet集合存储自定义对象Student2(String name,int age)
    要求:最终要按照年龄从小到大排序,并且当学生的姓名和年龄一样的时候要进行去重
    我们按照存储字符串一样的逻辑存储自定义对象,运行的时候报错了
    ClassCastException:
    com.shujia.day13.Student2 cannot be cast to java.lang.Comparable
    原因是因为,我们现在使用的时候自然排序方式,需要元素的类实现Comparable接口,才可以进行转型
 */
class Student implements Comparable<Student> {
    String name;
    int age;
    public Student() {
    }
    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) {
        //显式条件:按照年龄从大到小排序
        //this -- 待插入的元素
        //o -- 已经在树中的根
        int cha = o.age - this.age;
        return (cha == 0) ? o.name.compareTo(this.name):cha;
    }
}
public class TreeSetDemo2 {
    public static void main(String[] args) {
        //创建集合
        TreeSet<Student> treeSet = new TreeSet<>();
        //新建学生对象
        Student student1 = new Student("小一", 9);
        Student student2 = new Student("小二", 34);
        Student student3 = new Student("小三", 12);
        Student student4 = new Student("小四", 6);
        Student student5 = new Student("小五", 9);
        //添加元素
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        treeSet.add(student5);
        //遍历
        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}
上述代码Student类如果没有实现Comparable<>接口,就会报以下错误,原因是我们现在使用的时候自然排序方式,需要元素的类实现Comparable<>接口,才可以进行转型

注意: 重写Comparable<>接口中的compareTo()方法,可以让我们实现元素的排序问题
6.比较器排序:
在创建TreeSet对象的时候使用匿名内部类Comparator重写compareTo()方法
*
    比较器排序:
        public TreeSet(Comparator<? super E> comparator) {
            this(new TreeMap<>(comparator));
        }
    小总结:
        1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法,
            但是要保证元素的类实现Comparable接口,重写compareTo方法。
        2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法,
            传入一个实现了Comparator接口的子类对象,重写compare方法
 */
import java.util.Comparator;
import java.util.TreeSet;
class Student1 {
    String name;
    int age;
    public Student1() {
    }
    public Student1(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TreeSetDemo3 {
    public static void main(String[] args) {
        //创建集合
        TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //o1 -- 待插入的元素
                //o2 -- 已经存在树中的根
                int cha = o2.age - o1.age;
                return (cha == 0) ? o2.name.compareTo(o1.name):cha;
            }
        });
        //新建学生对象
        Student student1 = new Student("小一", 9);
        Student student2 = new Student("小二", 34);
        Student student3 = new Student("小三", 12);
        Student student4 = new Student("小四", 6);
        Student student5 = new Student("小五", 9);
        //添加元素
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        treeSet.add(student5);
        //遍历
        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}7.自然排序和比较器排序的底层源码:
public class TreeSet<E>{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    public TreeSet() {
        this(new TreeMap<E,Object>()); // this(..)
    }
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    // treeSet.add(s2);
    public boolean add(E e) {
        //TreeSet中add方法底层调用的是TreeMap中的put方法
        //e -- s2
        //PRESENT -- new Object()
        return m.put(e, PRESENT)==null;
    }
}
public class TreeMap{
    private final Comparator<? super K> comparator;
    private transient Entry<K,V> root;
    public TreeMap() {
        comparator = null;
    }
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    public V put(K key, V value) {
        // key -- "hadoop"
        // value -- new Object()
        Entry<K,V> t = root; // null
        //如果说是第一个元素插入进来,这个元素就作为红黑树的根保存
        if (t == null) {
            compare(key, key); // type (and possibly null) check
            root = new Entry<>(key, value, null); // s1
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator; // null
        //比较器排序
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //自然排序
        else {
            if (key == null) // s2
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t; // "java"
                cmp = k.compareTo(t.key); // 1
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
}8.TreeSet中的排序总结:
1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法, 但是要保证元素的类实现Comparable接口,重写compareTo方法。
2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法, 传入一个实现了Comparator接口的子类对象,重写compare方法。
七.Collection集合继承体系总结:

八.Map接口及其实现子类HasMap
1.概述:
(1)Map集合中的元素是一个键值对
(2)一个键对应一个值,键不允许重复,键是唯一的
(3)值可以发生重复
2.Map接口成员方法(一)
V remove(Object key) 根据键删除整个键值对,因为键是唯一的,返回键对应的值
V remove(Object key) 判断集合中是否包含键
boolean containsKey(Object key) 判断集合中是否包含键
boolean containsValue(Object value) 判断集合中是否包含值
boolean isEmpty() 判断集合是否为空
void clear() 清空集合
int size() 获取元素的个数,键值对的个数
演示:
import java.util.HashMap;
import java.util.Map;
/*
    Map集合继承体系:
        1、Map集合中的元素是一个键值对
        2、一个键对应一个值,键不允许重复,键是唯一的
        3、值可以发生重复
    子类:HashMap<K,V>
    成员方法:
        V put(K key,V value)
        V remove(Object key)
        void clear()
        boolean containsKey(Object key)
        boolean containsValue(Object value)
        boolean isEmpty()
        int size()
 */
public class MapDemo1 {
    public static void main(String[] args) {
        //创建HashMap的对象
        //HashMap()
        //构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
        HashMap<String, Integer> map1 = new HashMap<>();
        //V put(K key,V value) 向集合中添加一个元素键值对,如果键已经存在集合中,值会进行覆盖处理,返回被覆盖的值
        System.out.println(map1.put("小王", 19));    //null
        System.out.println("++++++++++++++++++++++++++++++++++++++++++");
        System.out.println(map1.put("小王", 33));      //19
        System.out.println(map1);         //{小王=33}
        map1.put("小李", 18);
        map1.put("小帅", 13);
        map1.put("小七", 12);
        //V remove(Object key) 根据键删除整个键值对,因为键是唯一的,返回键对应的值
        System.out.println(map1.remove("小七"));      //12
        System.out.println(map1);     //{小李=18, 小王=33, 小帅=13}
        //boolean containsKey(Object key)  //判断集合中是否包含键
        System.out.println(map1.containsKey("小"));     //false
        //boolean containsValue(Object value)   //判断集合中是否包含值
        map1.put("小九",12);
        System.out.println(map1.containsValue(12));  //true
        //void clear()          //清空集合
//        map1.clear();
//        System.out.println(map1);     //{}
        //boolean isEmpty()        //判断集合是否为空
        System.out.println(map1.isEmpty());
        //int size() 获取元素的个数,键值对的个数
        System.out.println(map1.size());
    }
}
3.与遍历有关的成员方法(二)
Set<K> keySet() 获取所有的键组成一个Set集合返回
Set<K> keySet() 获取所有的键组成一个Set集合返回
Collection<V> values() 获取所有的值组成一个Set集合返回
Set<Map.Entry<K,V>> entrySet() 获取所有的键值对组成一个Set集合返回
演示:
import java.util.Collection;
import java.util.HashMap;
import java.util.TreeSet;
/*
成员方法:
    V get(Object key)
    Set<K> keySet()
    Collection<V> values()
    Set<Map.Entry<K,V>> entrySet()
    Map集合遍历的方式:
        1、先获取所有的键,遍历键获取对应的值
        2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
 */
public class MapDemo2 {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1001,"曹操");
        map.put(1002,"曹丕");
        map.put(1003,"曹纯");
        map.put(1004,"曹叡");
        map.put(1005,"曹冲");
        //V get(Object key) 根据键获取值
        System.out.println(map.get(1001)); //曹操
        //Set<K> keySet() 获取所有的键组成一个Set集合返回
        System.out.println(map.keySet());        //[1001, 1002, 1003, 1004, 1005]
        //Collection<V> values() 获取所有的值组成一个Set集合返回
        Collection<String> values = map.values();
        System.out.println(values);             //[曹操, 曹丕, 曹纯, 曹叡, 曹冲]
        // Set<Map.Entry<K,V>> entrySet()  获取所有的键值对组成一个Set集合返回
        System.out.println(map.entrySet());     //[1001=曹操, 1002=曹丕, 1003=曹纯, 1004=曹叡, 1005=曹冲]
    }
}
4.Map集合遍历的方式:
(1)先获取所有的键,遍历键获取对应的值
(2)直接获取所有的键值对,遍历每一个键值对,再获得每一个键和值
演示:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
        Map集合遍历的方式:
        1、先获取所有的键,遍历键获取对应的值
        2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
 */
public class MapDemo3 {
    public static void main(String[] args){
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1001,"曹操");
        map.put(1002,"曹丕");
        map.put(1003,"曹纯");
        map.put(1004,"曹叡");
        map.put(1005,"曹冲");
        //方式一:先获取所有的键,遍历键获取对应的值
        Set<Integer> keys = map.keySet();
        for (Integer key : keys) {
            System.out.println(key+"=="+map.get(key));
        }
        System.out.println("---------------------------------------------");
        //方式二:先获取所有的键值对,遍历键值对获取键和值
        for (Map.Entry<Integer, String> keyValue : map.entrySet()) {
            System.out.println(keyValue.getKey()+"=="+keyValue.getValue());
        }
    }
}
九.TreeMap类
1.概述:
(1)TreeMap: 底层数据结构是红黑树
(2)根据创建时调用的构造方法不同,map中的键排序的规则不同
(3)创建TreeMap是无参构造方法的话,将来map中的键是以自然排序
(4)创建TreeMap是有参构造方法,传入Comparator<>接口的实现类对象(匿名内部类的方式)
演示:
import java.util.Comparator;
import java.util.TreeMap;
/*
    TreeMap: 底层数据结构是红黑树
    根据创建时调用的构造方法不同,map中的键排序的规则不同
    创建TreeMap是无参构造方法的话,将来map中的键是以自然排序
    创建TreeMap是有参构造方法,传入Comparator接口的实现类对象(匿名内部类的方式)
 */
public class TreeMapDemo1 {
    public static void main(String[] args) {
//        TreeMap<Integer, String> treeMap = new TreeMap<>();          //自然排序
        TreeMap<Integer, String> treeMap = new TreeMap<>(new Comparator<Integer>() {          //比较器排序
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        treeMap.put(1004, "小名");
        treeMap.put(1005, "小花");
        treeMap.put(1003, "小王");
        treeMap.put(1002, "小李");
        treeMap.put(1001, "小帅");
        System.out.println(treeMap);     //{1001=小帅, 1002=小李, 1003=小王, 1004=小名, 1005=小花}
    }
}
注意:所有的包装类都实现了Comparator<>接口,但是我们自己自定义的类必须要手动添加Comparator<>接口。
2.Map集合练习
(1)"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
import java.util.Set;
import java.util.TreeMap;
/*
 "aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
 */
public class TreeMapTest1 {
    public static void main(String[] args) {
        TreeMap<Character, Integer> treeMap = new TreeMap<>();
        String s = "aababcabcdabcde";
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (!treeMap.containsKey(c)) {
                treeMap.put(c, 1);
            } else {
                treeMap.put(c, treeMap.get(c) + 1);
            }
        }
        //遍历
        Set<Character> keys = treeMap.keySet();
        for (Character key : keys) {
//            System.out.print(key+"("+treeMap.get(key)+")");         //a(5)b(4)c(3)d(2)e(1)
        }
    }
}
(2)完成对HashMap嵌套ArrayList的遍历
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
    HashMap嵌套ArrayList
    每一个人都有一些爱好
    HashMap<Person,ArrayList<String>>
 */
class Person {
    String name;
    int age;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String
    toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TreeMapTest2 {
    public static void main(String[] args) {
        //创建学生对象
        Person person1 = new Person("小明", 23);
        Person person2 = new Person("小李", 33);
        Person person3 = new Person("小王", 13);
        //创建ArrayList集合对象,并添加爱好
        ArrayList<String> arrayList1 = new ArrayList<>();
        arrayList1.add("打羽毛球");
        arrayList1.add("打蓝球");
        ArrayList<String> arrayList2 = new ArrayList<>();
        arrayList2.add("养生");
        arrayList2.add("旅游");
        ArrayList<String> arrayList3 = new ArrayList<>();
        arrayList3.add("吃好吃的");
        arrayList3.add("和小伙伴玩");
        HashMap<Person, ArrayList> hashMap = new HashMap<>();
        hashMap.put(person1, arrayList1);
        hashMap.put(person2, arrayList2);
        hashMap.put(person3, arrayList3);
        //遍历
        Set<Map.Entry<Person, ArrayList>> keyValues = hashMap.entrySet();
        for (Map.Entry<Person, ArrayList> keyValue : keyValues) {
            for (Object habby : keyValue.getValue()) {
                System.out.println("姓名和年龄:"+keyValue.getKey() + " "+"爱好:" + habby);
            }
        }
    }
}
十.Collections类
1.概述:
针对集合操作的工具类
2.Collections成员方法
(1)public static <T> void sort(List<T> list) 对集合排序
(2)public static <T> int binarySearch(List<?> list,T key) 二分查找
(3)public static <T> T max(Collection<?> coll) 获取最大值
(4)public static void reverse(List<?> list) 使集合中元素逆序
(5)public static void shuffle(List<?> list) 将集合中的元素随机打乱
这里的shuffle和hadoop中的shuffle不是一个概念
注意:Collections工具类中的synchronizedXxx(xxx)方法可以将线程不安全的类变成线程安全的类,转换后使用方式不变
演示:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
    Collections工具类可以将线程不安全的类变成线程安全的类,使用方式不变
    synchronizedXxx(xxx)
 */
public class CollectionsDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        List<String> list1 = Collections.synchronizedList(list);
        list1.add("java");
        list1.add("java");
        list1.add("java");
        list1.add("java");
        System.out.println(list1);
    }
}

















