2.Collection集合2.1数组和集合的区别(理解)

  • 相同点

    都是容器,可以存储多个数据

  • 不同点

    • 数组的长度是不可变的,集合的长度是可变的

    • 数组可以存基本数据类型和引用数据类型

      集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类

2.2集合类体系结构(理解)

  • 红黑树添加节点后如何保持红黑规则

    • 根节点位置

      • 直接变为黑色

    • 非根节点位置

      • 父节点为黑色

        • 不需要任何操作,默认红色即可

      • 父节点为红色

        • 叔叔节点为红色

          1. 将”父节点”设为黑色,将”叔叔节点”设为黑色

          2. 将”祖父节点”设为红色

          3. 如果”祖父节点”为根节点,则将根节点再次变成黑色

        • 叔叔节点为黑色

          1. 将”父节点”设为黑色

          2. 将”祖父节点”设为红色

          3. 以”祖父节点”为支点进行旋转

  • 10.HashSet集合1.1HashSet集合概述和特点(应用)

    • 底层数据结构是哈希表

    • 存取无序

    • 不可以存储重复元素

    • 没有索引,不能使用普通for循环遍历

    1.2HashSet集合的基本应用(应用)


    public class HashSetDemo {
    public static void main(String[] args) {
    //创建集合对象
    HashSet<String> set = new HashSet<String>();

    //添加元素
    set.add("hello");
    set.add("world");
    set.add("java");
    set.add("java");
    set.add("java");
    set.add("java");

    //迭代器遍历
    Iterator<String> it = set.iterator();
    while (it.hasNext()) {
    String next = it.next();
    System.out.println(next);
    }

    System.out.println("=================");

    //增强for遍历
    for (String s : set) {
    System.out.println(s);
    }
    }
    }

    1.3哈希值(理解)

    • 哈希值简介

      ​ 根据对象的地址值或者属性值计算出来的一个int类型的整数

    • 如何获取哈希值

      ​ Object类中的public int hashCode():返回对象的哈希码值

    • 哈希值的特点

      • 如果没有重写hashcode方法,那么就根据对象的地址值进行计算哈希值

        针对同一个对象,返回的哈希值相同,针对不同的对象,返回的哈希值不同

      • 如果重写了hashcode方法,一般都是根据对象的属性值来进行重写。

        如果不同对象的属性值相同,那么它们的哈希值也是一样的。

    • 示例代码


      public class HashSetDemo2 {
      public static void main(String[] args) {
      Student s1 = new Student("张三",23);
      Student s2 = new Student("张三",23);
      Student s3 = new Student("李四",24);

      //如果Student类没有重写hashcode方法,则根据地址值计算哈希值
      //如果重写了则一般根据属性值进行计算哈希值
      System.out.println(s1.hashCode());
      System.out.println(s2.hashCode());
      System.out.println(s3.hashCode());
      }
      }

      public class Student {
      private String name;
      private int age;

      public Student() {
      }

      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 boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Student student = (Student) o;

      if (age != student.age) return false;
      return name != null ? name.equals(student.name) : student.name == null;
      }

      // 可以对Object类的hashCode()方法进行重写
      // 根据对象的属性值计算哈希值
      @Override
      public int hashCode() {
      int result = name != null ? name.hashCode() : 0;
      result = 31 * result + age;
      return result;
      }

      @Override
      public String toString() {
      return "Student{" +
      "name='" + name + '\'' +
      ", age=" + age +
      '}';
      }
      }

    1.4哈希表结构(理解)

    • JDK1.8以前

      ​ 数组 + 链表

      ​ 默认长度16,加载因子0.75 ,16*0.75=12 每次扩容2倍 头插法

    • JDK1.8以后

      ​ 尾插法(七上八下)

      • 节点个数小于等于8个

        ​ 数组 + 链表

      • 节点个数大于8个

        ​ 数组 + 红黑树

    1.5HashSet集合存储学生对象并遍历(应用)

    • 案例需求

      • 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合

      • 要求:学生对象的成员变量值相同,我们就认为是同一个对象

    • 代码实现

      学生类


      public class Student {
      private String name;
      private int age;

      public Student() {
      }

      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 boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Student student = (Student) o;

      if (age != student.age) return false;
      return name != null ? name.equals(student.name) : student.name == null;
      }

      @Override
      public int hashCode() {
      int result = name != null ? name.hashCode() : 0;
      result = 31 * result + age;
      return result;
      }
      }

      测试类


      /*
      创建一个存储学生对象的集合,存储多个学生对象,使用程序实现控制台遍历集合
      要求:学生对象的成员变量值相同,我们就认为是同一个对象。

      结论:HashSet集合存储自定义类型对象,那么必须重新hashcode和equals方法。
      */
      public class HashSetTest1 {
      public static void main(String[] args) {
      HashSet<Student> hs = new HashSet();
      hs.add(new Student("xiaohei", 23));
      hs.add(new Student("xiaohei", 23));
      hs.add(new Student("xiaomei", 23));
      for (Student s : hs) {
      System.out.println(s);
      }
      }
      }
    • 总结

      ​ HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法

    1.6 Set集合小结11.Map集合2.1Map集合概述和特点(理解)

    • interface Map K:键的类型;V:值的类型

    • 键不可以重复,值可以重复

    • 键和值一一对应,通过一个键能找到对应的值

    • 键和值这个整体,我们称之为键值对,或者又叫做Entry对象

    • 示例代码


      public class MyMap1 {
      public static void main(String[] args) {
      Map<String,String> map = new HashMap();

      map.put("itheima001","小智");
      map.put("itheima002","小美");
      map.put("itheima003","大胖");

      System.out.println(map);
      }
      }

    2.2Map集合的常用方法(应用)

    • 方法介绍

      方法名说明
      V put(K key,V value)添加元素,当key存在时进行替换
      V remove(Object key)根据键删除键值对元素
      void clear()移除所有的键值对元素
      boolean containsKey(Object key)判断集合是否包含指定的键
      boolean containsValue(Object value)判断集合是否包含指定的值
      boolean isEmpty()判断集合是否为空
      int size()集合的长度,也就是集合中键值对的个数
    • 示例代码


      public class MyMap2 {
      public static void main(String[] args) {
      Map<String,String> map = new HashMap();
      map.put("itheima001","小智");
      map.put("itheima002","小美");
      map.put("itheima003","大胖");
      map.put("itheima004","小黑");
      map.put("itheima005","大师");

      //V put(K key,V value) 添加元素
      method1(map);
      //V remove(Object key) 根据键删除键值对元素
      method2(map);
      //void clear() 移除所有的键值对元素
      method3(map);
      //boolean containsKey(Object key) 判断集合是否包含指定的键
      method4(map);
      //boolean containsValue(Object value) 判断集合是否包含指定的值
      method5(map);
      //boolean isEmpty() 判断集合是否为空
      method6(map);
      //int size() 集合的长度,也就是集合中键值对的个数
      method7(map);
      }

      private static void method7(Map<String, String> map) {
      //int size() 集合的长度,也就是集合中键值对的个数
      int size = map.size();
      System.out.println(size);
      }

      private static void method6(Map<String, String> map) {
      //boolean isEmpty() 判断集合是否为空
      boolean empty1 = map.isEmpty();
      System.out.println(empty1);//false

      map.clear();
      boolean empty2 = map.isEmpty();
      System.out.println(empty2);//true
      }

      private static void method5(Map<String, String> map) {
      //boolean containsValue(Object value) 判断集合是否包含指定的值
      boolean result1 = map.containsValue("aaa");
      boolean result2 = map.containsValue("小智");
      System.out.println(result1);
      System.out.println(result2);
      }

      private static void method4(Map<String, String> map) {
      //boolean containsKey(Object key) 判断集合是否包含指定的键
      boolean result1 = map.containsKey("itheima001");
      boolean result2 = map.containsKey("itheima006");
      System.out.println(result1);
      System.out.println(result2);
      }

      private static void method3(Map<String, String> map) {
      //void clear() 移除所有的键值对元素
      map.clear();
      System.out.println(map);
      }

      private static void method2(Map<String, String> map) {
      //V remove(Object key) 根据键删除键值对元素
      String s = map.remove("itheima001");
      System.out.println(s);
      System.out.println(map);
      }

      private static void method1(Map<String, String> map) {
      //V put(K key,V value) 添加元素
      //如果要添加的键不存在,那么会把键值对都添加到集合中
      //如果要添加的键是存在的,那么会覆盖原先的值,把原先值当做返回值进行返回。
      String s = map.put("itheima001", "aaa");
      System.out.println(s);
      System.out.println(map);
      }
      }

    2.3Map集合的遍历方法(应用)

    • 方法介绍

      方法名说明
      V get(Object key)根据键获取值
      Set keySet()获取所有键的集合
      Set<Map.Entry> entrySet()获取所有键值对对象的集合
      K getKey()通过entry对象获取键
      V getValue()通过entry对象获取值

    2.4Map集合的遍历(方式1)(应用)

    • 遍历思路

      • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合

        • 把所有的丈夫给集中起来

        • 遍历丈夫的集合,获取到每一个丈夫

        • 根据丈夫去找对应的妻子

    • 步骤分析

      • 获取所有键的集合。用keySet()方法实现

      • 遍历键的集合,获取到每一个键。用增强for实现

      • 根据键去找值。用get(Object key)方法实现

    • 代码实现


      public class MyMap3 {
      public static void main(String[] args) {
      //创建集合并添加元素
      Map<String,String> map = new HashMap();
      map.put("1号丈夫","1号妻子");
      map.put("2号丈夫","2号妻子");
      map.put("3号丈夫","3号妻子");
      map.put("4号丈夫","4号妻子");
      map.put("5号丈夫","5号妻子");

      //获取到所有的键
      Set<String> keys = map.keySet();
      //遍历Set集合得到每一个键
      for (String key : keys) {
      //通过每一个键key,来获取到对应的值
      String value = map.get(key);
      System.out.println(key + "---" + value);
      }
      }
      }

    2.5Map集合的遍历(方式2)(应用)

    • 遍历思路

      • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合

        • 获取所有结婚证的集合

        • 遍历结婚证的集合,得到每一个结婚证

        • 根据结婚证获取丈夫和妻子

    • 步骤分析

      • 获取所有键值对对象的集合

        • Set<Map.Entry> entrySet():获取所有键值对对象的集合

      • 遍历键值对对象的集合,得到每一个键值对对象

        • 用增强for实现,得到每一个Map.Entry

      • 根据键值对对象获取键和值

        • 用getKey()得到键

        • 用getValue()得到值

    • 代码实现


      public class MyMap4 {
      public static void main(String[] args) {
      //创建集合并添加元素
      Map<String, String> map = new HashMap();
      map.put("1号丈夫", "1号妻子");
      map.put("2号丈夫", "2号妻子");
      map.put("3号丈夫", "3号妻子");
      map.put("4号丈夫", "4号妻子");
      map.put("5号丈夫", "5号妻子");

      //首先要获取到所有的键值对对象。
      //Set集合中装的是键值对对象(Entry对象)
      //而Entry里面装的是键和值
      Set<Map.Entry<String, String>> entries = map.entrySet();

      //遍历Set集合,得到每一个键值对对象
      for (Map.Entry<String, String> entry : entries) {
      //通过entry对象的getKey获取键,getValue()获取值
      String key = entry.getKey();
      String value = entry.getValue();
      System.out.println(key + "---" + value);
      }
      }
      }

    12.HashMap集合3.1HashMap集合概述和特点(理解)

    • HashMap底层是哈希表结构的

    • 依赖hashCode方法和equals方法保证键的唯一

    • 如果键要存储的是自定义对象,需要重写hashCode和equals方法

    • 底层结构与HashSet一样

    3.2HashMap集合应用案例(应用)

    • 案例需求

      • 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。

      • 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象

    • 代码实现

      学生类


      public class Student {
      private String name;
      private int age;

      public Student() {
      }

      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 boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Student student = (Student) o;

      if (age != student.age) return false;
      return name != null ? name.equals(student.name) : student.name == null;
      }

      @Override
      public int hashCode() {
      int result = name != null ? name.hashCode() : 0;
      result = 31 * result + age;
      return result;
      }
      }

      测试类


      public class MyMap5 {
      public static void main(String[] args) {
      HashMap<Student,String> hm = new HashMap();

      Student s1 = new Student("xiaohei",23);
      Student s2 = new Student("dapang",24);
      Student s3 = new Student("xiaomei",22);

      hm.put(s1,"江苏");
      hm.put(s2,"北京");
      hm.put(s3,"天津");

      //第一种:先获取到所有的键,再通过每一个键来找对应的值
      Set<Student> keys = hm.keySet();
      for (Student key : keys) {
      String value = hm.get(key);
      System.out.println(key + "----" + value);
      }

      System.out.println("---------------------------------");

      //第二种:先获取到所有的键值对对象。再获取到里面的每一个键和每一个值
      Set<Map.Entry<Student, String>> entries = hm.entrySet();
      for (Map.Entry<Student, String> entry : entries) {
      Student key = entry.getKey();
      String value = entry.getValue();
      System.out.println(key + "----" + value);
      }

      System.out.println("---------------------------------");

      //第三种:
      hm.forEach(
      (Student student, String s) -> {
      System.out.println(student + "---" + s);
      }
      );

      }
      }

    13.TreeMap集合4.1TreeMap集合概述和特点(理解)

    • TreeMap底层是红黑树结构

    • 依赖自然排序或者比较器排序,对键进行排序

    • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则

    4.2TreeMap集合应用案例(应用)

    • 案例需求

      • 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历

      • 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序

    • 代码实现

      学生类


      public class Student implements Comparable<Student>{
      private String name;
      private int age;

      public Student() {
      }

      public Student(String name, int age) {
      this.name = name;
      this.age = age;
      }

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }

      public int getAge() {
      return age;
      }

      public void setAge(int age) {
      this.age = age;
      }

      @Override
      public String toString() {
      return "Student{" +
      "name='" + name + '\'' +
      ", age=" + age +
      '}';
      }

      @Override
      public int compareTo(Student o) {
      //按照年龄进行排序
      int result = o.getAge() - this.getAge();
      //次要条件,按照姓名排序。
      result = result == 0 ? o.getName().compareTo(this.getName()) : result;
      return result;
      }
      }

      测试类


      public class Test {
      public static void main(String[] args) {
      // 使用自然排序,要求键必须实现Comparable接口
      // TreeMap tm = new TreeMap();

      // 使用比较器排序
      TreeMap<Student,String> tm = new TreeMap(new Comparator<Student>() {
      @Override
      public int compare(Student o1, Student o2) {
      int result = o1.getAge() - o2.getAge();
      return (result== 0) ? o1.getName().compareTo(o2.getName()) : result;
      }
      });

      Student s1 = new Student("xiaohei",23);
      Student s2 = new Student("dapang",22);
      Student s3 = new Student("xiaomei",22);

      tm.put(s1,"江苏");
      tm.put(s2,"北京");
      tm.put(s3,"天津");

      tm.forEach(
      (Student key, String value)->{
      System.out.println(key + "---" + value);
      }
      );
      }
      }

    14.可变参数

    • 可变参数介绍

      • JDK5新特性

    • 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

    • 方法的参数类型已经确定,个数不确定,我们可以使用可变参数

    • 可变参数定义格式


      修饰符 返回值类型 方法名(数据类型… 变量名) { }
    • 可变参数的注意事项

      • 这里的变量其实是一个数组

      • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后

    • 可变参数的基本使用


      /**
      * 需求:定义一个方法求N个数的和
      * 可变参数实现
      */
      public class MyVariableParameter3 {
      public static void main(String[] args) {

      int sum1 = getSum(1, 2);
      System.out.println(sum1);

      int sum2 = getSum(1, 2, 3);
      System.out.println(sum2);
      }

      public static int getSum(int... arr) {
      //说明可变数组 底层就是一个一维数组 这里是java的一个语法糖
      // 什么叫语法糖? 一种语法 ,这种语法对功能没有影响,主要方便程序员开发和阅读。
      // System.out.println(arr);//[I@10f87f48
      int sum = 0;
      for (int i = 0; i < arr.length; i++) {
      sum = sum + arr[i];
      }
      return sum;
      }
      }

    15.创建不可变集合

    • 方法介绍

      • JDK9新特性

    • 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合

      • 这个集合不能添加,不能删除,不能修改

      • 但是可以结合集合的带参构造,实现集合的批量添加

    • 在Map接口中,还有一个ofEntries方法可以提高代码的阅读性

      • 首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中

    • 示例代码


      public class MyVariableParameter4 {
      public static void main(String[] args) {
      // method1();
      // method2();
      // method3();
      // method4();
      }

      private static void method4() {
      Map<String, String> map = Map.ofEntries(
      Map.entry("zhangsan", "江苏"),
      Map.entry("lisi", "北京"));
      System.out.println(map);
      }

      private static void method3() {
      Map<String, String> map = Map.of("zhangsan", "江苏", "lisi", "北京", "wangwu", "天津");
      System.out.println(map);
      }

      private static void method2() {
      //传递的参数当中,不能存在重复的元素。
      Set<String> set = Set.of("a", "b", "c", "d","a");
      System.out.println(set);
      }

      private static void method1() {
      List<String> list = List.of("a", "b", "c", "d");
      System.out.println(list);
      //list.add("Q");
      //list.remove("a");
      //list.set(0,"A");
      //System.out.println(list);

      //集合的批量添加。
      //首先是通过调用List.of方法来创建一个不可变的集合,of方法的形参就是一个可变参数。
      //再创建一个ArrayList集合,并把这个不可变的集合中所有的数据,都添加到ArrayList中。
      ArrayList<String> list3 = new ArrayList(List.of("a", "b", "c", "d"));
      list3.add("1");
      System.out.println(list3);
      }
      }

    16.集合总结

    • 扩展一

      LinkedHashSet和LinkedHashMap: 可以实现去重并且存取有序的效果


      /**
      * LinkedHashSet和LinkedHashMap: 可以实现去重并且存取有序的效果
      */
      public class LinkedHashSetDemo {
      public static void main(String[] args) {
      HashSet<String> hashSet = new HashSet();
      hashSet.add("fff");
      hashSet.add("aaa");
      hashSet.add("bbb");
      hashSet.add("ccc");
      hashSet.add("ddd");
      hashSet.add("ddd");
      System.out.println("hashSet" + hashSet);

      LinkedHashSet<String> linkedHashSet = new LinkedHashSet();
      linkedHashSet.add("fff");
      linkedHashSet.add("aaa");
      linkedHashSet.add("bbb");
      linkedHashSet.add("ccc");
      linkedHashSet.add("ddd");
      linkedHashSet.add("ddd");
      System.out.println("linkedHashSet" + linkedHashSet);

    HashMap<String, Integer> hashMap = new HashMap();
    hashMap.put("fff", 1);
    hashMap.put("aaa", 1);
    hashMap.put("bbb", 1);
    hashMap.put("ccc", 1);
    hashMap.put("ddd", 1);
    System.out.println("hashMap" + hashMap);


    LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap();
    linkedHashMap.put("fff", 1);
    linkedHashMap.put("aaa", 1);
    linkedHashMap.put("bbb", 1);
    linkedHashMap.put("ccc", 1);
    linkedHashMap.put("ddd", 1);
    System.out.println("linkedHashMap" + linkedHashMap);
    }

    }




    - 扩展二

    // Java约定:如果二个对象equlas相等,那么hashcode也一定要相等,但是二个对象hashcode相等,equlas则不一定要相等。
    public class HashSetDemo2 {
    public static void main(String[] args) {
    Student s1 = new Student("张三",23);
    Student s2 = new Student("张三",23);
    Student s3 = new Student("李四",24);

    //如果Student类没有重写hashcode方法,则根据地址值计算哈希值
    //如果重写了则一般根据属性值进行计算哈希值
    System.out.println(s1.hashCode());
    System.out.println(s2.hashCode());
    System.out.println(s3.hashCode());

    System.out.println("-----------------------------");

    // Java约定:如果二个对象equlas相等,那么hashcode也一定要相等,
    // 但是二个对象hashcode相等,equlas则不一定要相等。
    //大部分情况不同的属性值计算出的哈希值都是不同的。但是也有例外
    Student s4 = new Student("重地",23);
    Student s5 = new Student("通话",23);
    System.out.println(s4.hashCode()); // 36561268
    System.out.println(s5.hashCode()); // 36561268
    }
    }