1 泛型机制

image.png

  • 集合的右侧使用<数据类型>的方式来明确要求集合中可以存放的对象

image.png

  • 泛型的本质就是参数化类型
  • 实际参数可以传递各种各样广泛的数据类型, 因此得名为泛型.

image.png
image.png
image.png

  1. <T> T[] toArray(T[] a);
  1. package com.lagou.task15;
  2. /**
  3. * @author 西风月
  4. * @date 2020/7/30
  5. * @description 自定义泛型类Person
  6. * 其中T相当于形式参数负责占位,具体数值由实参实现
  7. */
  8. public class Person<T> {
  9. private String name;
  10. private int age;
  11. private T gender;
  12. public Person(String name, int age, T gender) {
  13. this.name = name;
  14. this.age = age;
  15. this.gender = gender;
  16. }
  17. public Person() {
  18. }
  19. @Override
  20. public String toString() {
  21. return "Person{" +
  22. "name='" + name + '\'' +
  23. ", age=" + age +
  24. ", gender=" + gender +
  25. '}';
  26. }
  27. //自定义方法实现将参数指定数组中的所有元素打印出来
  28. public static <T1> void printArray(T1[] arr) { //返回值前加 <T1>
  29. for (T1 tt: arr) {
  30. System.out.println("tt = " + tt);
  31. }
  32. }
  33. public String getName() {
  34. return name;
  35. }
  36. public void setName(String name) {
  37. this.name = name;
  38. }
  39. public int getAge() {
  40. return age;
  41. }
  42. public void setAge(int age) {
  43. this.age = age;
  44. }
  45. //不是泛型方法
  46. public /*static*/ T getGender() {
  47. return gender;
  48. }
  49. public void setGender(T gender) {
  50. this.gender = gender;
  51. }
  52. }
package com.lagou.task15;

/**
 * @author 西风月
 * @date 2020/7/30
 * @description
 */
//public class SubPerson extends Person { //丢弃泛型 不保留泛型并且没有指定类型, 此时T默认类型 Object  擦除
//public class SubPerson extends Person<String> { //不保留泛型, 但是指定了泛型的类型,此时Person中的T被指定为String类型
//public class SubPerson<T> extends Person<T> { //保留父类的泛型, 可以在构造对象时指定T的类型
public class SubPerson<T, T1> extends Person<T> { //保留父类的泛型, 同时在子类增加新的泛型
}
package com.lagou.task15;

/**
 * @author 西风月
 * @date 2020/7/30
 * @description
 */
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person("liubei", 30, "男");
        System.out.println(p1);

        Person<String> p2 = new Person<>();
        p2.setGender("女");
        System.out.println(p2);

        Person<Boolean> p3 = new Person<>("libei", 30, true);
        p3.setGender(true);
        System.out.println(p3);

        //call泛型方法
        Integer[] arr = {11, 22, 23, 88, 99};
        Person.printArray(arr);
    }
}

image.png
image.png

? 可以代表任意类型,所以主要用于声明时的限制情况。
当考虑到要add时候,对于安全性的考虑,应该使用泛型的<? super XXX>模式,
当考虑到要对list进行get类型显示的时候,就要考虑用到<? extends XXX>,这样类型安全并且简洁.
extends 可用于的返回类型限定,不能用于参数类型限定。
super 可用于参数类型限定,不能用于返回类型限定。
package com.lagou.task15;

import java.util.LinkedList;
import java.util.List;

/**
 * @author 西风月
 * @date 2020/7/30
 * @description
 */
public class GenericTest {
    public static void main(String[] args) {
        List<Animal> lt1 = new LinkedList<>();
        List<Dog> lt2 = new LinkedList<>();
        //试图梁lt2的数值赋值给lt1, 也就是发生List<Dog>类型向List<Animal>类型的转换
        //lt1 = lt2;

        // 使用通配符作为泛型类型的公共父类
        List<?> lt3 = new LinkedList<>();
        lt3 = lt1;    //List<Animal>  --> List<?>
        lt3 = lt2;    //List<Dog>     --> List<?>

        //向公共父类中增加元素与获取元素
        //lt3.add(new Animal()); // Error:不能存放Animal类型的对象
        //lt3.add(new Dog()); Error:不能存放Dog类型的对象,不支持元素的添加操作
        Object o = lt3.get(0); //ok 支持元素的获取, 返回值全部当做Object类型处理

        System.out.println("--------------------------------------");
        //使用有限制的通配符进行使用
        List<? extends Dog> lt4 = new LinkedList<>();
        //lt4.add(new Animal());
        //lt4.add(new Dog());
        //lt4.add(new Object());
        Animal animal = lt4.get(0);
        System.out.println("--------------------------------------");
        List<? super Animal> lt5 = new LinkedList<>();
        lt5.add(new Animal());
        lt5.add(new Dog());
        //lt5.add(new Object()); //Error :超过了Animal的范围
        Object object = lt5.get(0);
    }
}

2. Set集合(熟悉)

1. 基本概念

image.png

  • Set集合没有重复 且 没有先后顺序, 但是不表示无序
  • HashSet 哈希表进行数据管理

    • 哈希表其实就是一维数组,数组元素时单向链表(链表数组)

  • 哈希表

image.png

2. 常用的方法

image.png

3. 元素放入HashSet集合的原理

image.png
image.png
疑问:
HashSet类里增加元素时, 当两个元素调用equals方法相等时证明这两个元素相同
重写hashCode方法保证这两个元素得到的哈希码值相同, 由同一个哈希算法生成的索引位置相同
此时遍历该索引位置对应的单向链表, 若比较后发现均不相同, 则在链表尾部新增元素.
这里有个疑问, 既然hashCode都相等了, 为什么还要判断equals是否相同等, 是考虑到泛型吗?

两个对象相等,hashcode一定相等
两个对象不等,hashcode不一定不等
hashcode相等,两个对象不一定相等
hashcode不等,两个对象一定不等

4. TreeSet集合的概念

image.png

  • 二叉树

04 二叉树的基本概念.png

  • 有序的二叉树

05 有序二叉树的基本概念.png

  • 红黑树也是有序的二叉树

06 红黑树.png

  • TreeSet集合的底层是采用红黑树进行数据管理, 当有新元素插入到TreeSet集合时,需要使用新元素与集合中的已有的元素比较来确定元素的合理位置
  • 比较元素大小的规则有两种:
  • (1) 自然排序规则
    • 元素类型需事先 java.lang.Comparable接口
  • (2) 比较器规则进行排序
    • 构造TreeSet集合时传入java.util.Comparator接口 ```java package com.lagou.task15;

import java.util.Set; import java.util.TreeSet;

/**

  • @author 西风月
  • @date 2020/7/31
  • @description */ public class TreeSetTest { public static void main(String[] args) {

     //1. 准备一个TreeSet集合并打印
     Set<String> s1 = new TreeSet<>();
     System.out.println("s1 = " + s1);
    
     //2. 向集合中添加String类型的对象并打印
     System.out.println(s1.add("aa"));
     System.out.println("s1 = " + s1);
    
     System.out.println(s1.add("cc"));
     System.out.println("s1 = " + s1);
    
     System.out.println(s1.add("bb"));
     //由于TreeSet集合中的内部是采用红黑树实现的,因此元素有大小的次序, 默认从小到大打印
     System.out.println("s1 = " + s1);
    

    } }


- String类其实已经实现了Comparable<String>接口

![image.png](https://cdn.nlark.com/yuque/0/2020/png/1567843/1596210746508-303e3017-15fc-4675-b7c6-be1edd0b2e9b.png#align=left&display=inline&height=60&margin=%5Bobject%20Object%5D&name=image.png&originHeight=67&originWidth=586&size=6270&status=done&style=none&width=526)

```java
package com.lagou.task15;

/**
 * @author 西风月
 * @date 2020/8/1
 * @description
 */
public class Student implements Comparable<Student> {
    private String name;
    private int age;

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

    public Student() {
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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 int compareTo(Student o) {
        //return 0;  //调用对象和参数对象相等, 调用对象就是新增加的对象
        //return -1; //后来的都小于前面的,放在前面
        //return 1;  //后来的都大于前面的,放在后面
        //return this.getName().compareTo(o.getName()); //比较姓名
        //return this.getAge() - o.getAge();
        /*int i = this.getName().compareTo(o.getName());
        if(i == 0)
            return this.getAge() - o.getAge();
        return i;*/
        int ia = this.getName().compareTo(o.getName());
        return 0 == ia ? this.getAge() - o.getAge() : ia;
    }
}
package com.lagou.task15;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author 西风月
 * @date 2020/7/31
 * @description
 */
public class TreeSetTest {
    public static void main(String[] args) {
        //1. 准备一个TreeSet集合并打印
        Set<String> s1 = new TreeSet<>();
        System.out.println("s1 = " + s1);

        //2. 向集合中添加String类型的对象并打印
        System.out.println(s1.add("aa"));
        System.out.println("s1 = " + s1);

        System.out.println(s1.add("cc"));
        System.out.println("s1 = " + s1);

        System.out.println(s1.add("bb"));
        //由于TreeSet集合中的内部是采用红黑树实现的,因此元素有大小的次序, 默认从小到大打印
        System.out.println("s1 = " + s1);

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

        //4. 准备一个比较器对象作为参数传递给构造方法
        //匿名内部类 接口/父类类型 引用变量名 = new 类型() {方法的重写};
        /*Comparator<Student> comparator = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) { //o1表示新增加的对象, o2表示集合中已有的对象
                return o1.getAge() - o2.getAge();
                //return 0;
            }
        };*/

        //从Java8开始支持Lambda表达式  (参数列表) -> {方法体}
        Comparator<Student> comparator = (Student o1, Student o2) -> {return o1.getAge() - o2.getAge();};

        //3. 准备一个TreeSet放入自定义类型的对象
        //Set<Student> s2 = new TreeSet<>();
        Set<Student> s2 = new TreeSet<>(comparator);   //比较器优先于自然排序
        System.out.println(s2.add(new Student("liubei", 40)));
        System.out.println(s2.add(new Student("guanyu", 38)));
        System.out.println(s2.add(new Student("zhangfei", 29)));

        //java.lang.ClassCastException: class com.lagou.task15.Student cannot be cast to class java.lang.Comparable
        System.out.println("s2 = " + s2);
    }
}

3. Map集合(重点)

1. 基本概念

image.png

image.png

  • HashSet的也是用HashMap实现的 ```java public HashSet() { map = new HashMap<>(); }

public boolean add(E e) { return map.put(e, PRESENT)==null; } private static final Object PRESENT = new Object();

<a name="xH47G"></a>
## 2. 常用的方法
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1567843/1596269891420-68abc17d-4d7d-41e7-8d48-676897c70bbb.png#align=left&display=inline&height=494&margin=%5Bobject%20Object%5D&name=image.png&originHeight=554&originWidth=814&size=246108&status=done&style=none&width=726)<br />默认加载因子是 0.75 初始容量是16<br />默认加载因子与初始容量的乘积是12

- 介绍了HashMap初始化与add元素时扩容的原理:
```java
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted  //加载因子
}
public V put(K key, V value) {
    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;
    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;
        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;
}

image.png
类似于Set的增加元素流程,若已存在相同K值得元素, 则更新V后返回OldV
image.png

package com.lagou.task15;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 西风月
 * @date 2020/8/1
 * @description
 */
public class MapCountTest {
    public static void main(String[] args) {
        String str = "123,456,789,123,456";
        Map<String, Integer> m1 = new HashMap<>();
        int i = 0;
        for (String s: str.split(",")) {
            m1.merge(s, 1, Integer::sum);
        }

        System.out.println(m1);
    }
}
package com.lagou.task15;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author 西风月
 * @date 2020/8/1
 * @description
 */
public class MapTest {
    public static void main(String[] args) {
        //1. 准备一个Map集合并打印
        Map<String, String> m1 = new HashMap<>();
        System.out.println("m1 = " + m1);

        System.out.println(m1.put("1", "One"));
        System.out.println("m1 = " + m1);

        System.out.println(m1.put("2", "Two"));
        System.out.println("m1 = " + m1);

        System.out.println(m1.put("3", "Three"));
        System.out.println("m1 = " + m1);

        System.out.println(m1.put("1", "Eleven"));  //返回原来覆盖前的值
        System.out.println("m1 = " + m1);

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

        System.out.println(m1.containsKey("11"));
        System.out.println(m1.containsKey("1"));
        System.out.println(m1.containsValue("One"));
        System.out.println(m1.containsValue("Eleven"));
        System.out.println(m1.get("5"));
        System.out.println(m1.get("3"));

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

        System.out.println(m1.remove("1"));  //Eleven
        System.out.println("m1 = " + m1);  //m1 = {2=Two, 3=Three}
        System.out.println(m1.keySet());  //返回此方法中键值的Set视图  对Map集合的遍历

        for (String ts:  m1.keySet()) {
            System.out.println(ts + "=" + m1.get(ts));
        }

        System.out.println("----------------------------------------");
        // 获取Map集合中所有的Value并组成Collection视图
        Collection<String> co = m1.values();
        for(String ts : co) {
            System.out.println("ts = " + ts);
        }

        System.out.println("----------------------------------------");
        //获取Map集合中所有的键值对
        Set<Map.Entry<String, String>> entries = m1.entrySet();
        for( Map.Entry<String, String> me : entries) {
            System.out.println(me);
        }
    }
}

4. Collections类

1 基本概念

image.png

2. 常用的方法有

image.png
image.png

package com.lagou.task15;

import java.io.Serializable;
import java.util.*;

/**
 * @author 西风月
 * @date 2020/8/1
 * @description
 */
public class CollectionsTest {
    public static void main(String[] args) {
        List<Integer> lt1 = Arrays.asList(11, 22, 23, 64);
        System.out.println(Collections.max(lt1));
        System.out.println(Collections.min(lt1));


        Collections.reverse(lt1);
        System.out.println(lt1);

        /*Collections.shuffle(lt1);
        System.out.println(lt1);*/

        Collections.sort(lt1);
        System.out.println(lt1);
        //List<Integer>lt2 = new ArrayList<>(20);
        List<Integer>lt2 = Arrays.asList(new Integer[10]);
        System.out.println(lt1.size());
        System.out.println(lt2.size());
        Collections.copy(lt2, lt1);
        System.out.println(lt2);
    }
}

总结

  • 泛型机制(熟悉)
    • 概念和本质、自定义泛型接口、类、方法、继承方面的体现、通配符
  • Set集合(熟悉)
    • 概念、主要实现类、常用的方法、TreeSet类两种方式的规则
  • Map集合(重点)
    • 概念、主要实现类、常用的方法 && 遍历方式
  • Collections工具类
    • 概念和常用的方法