文章目录
- 补充知识-泛型
- 版本信息
- 泛型类
- 常见泛型标识符
- 泛型应用实例
- 注意事项
 
- 泛型方法
- 非静态的泛型方法
- 静态的泛型方法:必须声明出自己独立的泛型
 
- 泛型接口
- 泛型通配符
 
- 补充知识-树
- 基本概念
- 二叉树-普通二叉树
- 二叉树-二叉查找树
- 添加节点流程
- 优点、不足
 
- 二叉树-平衡二叉树
- 旋转机制
 
- 二叉树-红黑树
- 红黑规则
 
 
- 补充知识-常见数据结构
- 栈
- 队列
- 链表
- 单项链表和双向链表
- 集合底层数据结构
- Collection-List集合
- Collection-Set集合
- Map集合
 
 
- 集合
- 集合分类
- 大分类
- 小分类
- Collection接口的子类
- Map接口的子类
 
 
- 集合的遍历方式
- 通用遍历方式
- 迭代器
- 增强for循环
- forEach方法
 
- 额外遍历方式-List集合
- 普通for循环
- ListIterator(List集合特有的迭代器)
 
 
- Collection接口--通用函数
- Collection接口--List接口-ArrayList
- ArrayList长度可变原理
 
- Collection接口--List接口-LinkedList
- Collection接口--Set接口-TreeSet
- 两种排序规则-自然排序
- 常见类排序:String,默认增序排序
- 自定义类排序
 
- 两种排序规则-比较器排序
- 两种排序规则优先级
 
- Collection接口--Set接口-HashSet
- 版本信息
- 数据存储过程
- 数组扩容
- 链表转红黑树
- 特点
 
- Collection接口--Set接口-LinkedHashSet
- Collection接口--总结
- 类型选用
- Collections工具类
 
- Map接口
- 基础介绍
- Map类别
- 通用方法
- 底层原理-HashMap举例
- 遍历方式
- 通过forEach方法遍历
- 通过键值对对象获取键和值
- 通过键找值
 
- Map集合选用
 
补充知识-泛型
版本信息
JDK5引入, 可以在编译阶段约束操作的数据类型, 并进行检查
- 即统一数据类型,将运行时期的错误提升到编译期
泛型类
不知道使用者想要添加什么类型的数据,就给出一个泛型类
常见泛型标识符
| E | T | K | V | 
|---|---|---|---|
| Element | Type | Key | Value | 
| 表示集合的元素类型 | 表示任意类型 | 表示关键字类型 | 表示值类型 | 
泛型应用实例
package com.itheima.day10.generics;
import java.util.ArrayList;
public class GenericsDemo2 {
    public static void main(String[] args) {
        Student<Integer> stu = new Student<>(); // 正确
        Student<int> stu = new Student<>();   // 错误,泛型类只能是引用数据类型
    }
}
class Student<E> {
    private E e;
    public E getE() {
        return e;
    }
    public void setE(E e) {
        this.e = e;
    }
}
注意事项
-  当创建对象的时候,才能确定泛型类具体的类别 
-  !!!泛型类只能写引用数据类型 
泛型方法
非静态的泛型方法
-  根据类的泛型去匹配 
-  上述 getE方法、setE方法 
静态的泛型方法:必须声明出自己独立的泛型
- 静态方法随着类的加载而加载,类还没创建,就没有具体类型,静态方法就有问题了。所以要声明自己独立的类型
- 在调用方法,传入具体参数的时候,确定到静态方法具体的泛型类型
package com.itheima.day10.generics;
public class GenericsDemo3 {
    public static void main(String[] args) {
        String[] arr1 = {"张三", "李四", "王五"};
        Integer[] arr2 = {11, 22, 33};
        Double[] arr3 = {11.1, 22.2, 33.3};
        printArray(arr1);
        printArray(arr2);
        printArray(arr3);
    }
    // static后边的T
    public static <T> void printArray(T[] arr) {
        System.out.print("[");
        for (int i = 0; i < arr.length - 1; i++) {
            System.out.print(arr[i] + ", ");
        }
        System.out.println(arr[arr.length - 1] + "]");
    }
}
泛型接口
类实现接口的时候,接口带有泛型
- 类实现接口的时候,直接确定类型
- 延续接口的泛型,等创建对象的时候确定
package com.itheima.day10.generics;
import java.util.ArrayList;
import java.util.List;
public class GenericsDemo4 {
    public static void main(String[] args) {
        InterBImpl<String> i = new InterBImpl<>();
    }
}
interface Inter<E> {
    void show(E e);
}
// 类实现接口的时候,直接确定类型
class InterAImpl implements Inter<String> {
    @Override
    public void show(String s) {
    }
}
// 延续接口的泛型,等创建对象的时候确定
class InterBImpl<E> implements Inter<E>{
    @Override
    public void show(E e) {
    }
}
泛型通配符
想要一个方法可以传入不同的对象类型
| ? | ? extends E | ? super E | 
|---|---|---|
| 任意类型 | E及E的子类 | E及E的父类 | 
package com.itheima.day10.generics;
import java.util.ArrayList;
public class GenericsDemo5 {
    /*
        泛型通配符
                ? : 任意类型
                ? extends E : 可以传入的是E, 或者是E的子类
                ? super E : 可以传入的是E, 或者是E的父类
     */
    public static void main(String[] args) {
        ArrayList<Coder> list1 = new ArrayList<>();
        list1.add(new Coder());
        ArrayList<Manager> list2 = new ArrayList<>();
        list2.add(new Manager());
        method(list1);
        method(list2);
    }
    public static void method(ArrayList<? extends Employee> list){
        for (Employee o : list) {
            o.work();
        }
    }
    
    public static void method1(ArrayList<? super Employee> list){
        for (Object A : list) {
            Employee o = (Employee)A;
            o.work();
        }
    }    
}
abstract class Employee {
    private String name;
    private double salary;
    public Employee() {
    }
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    public abstract void work();
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public String toString() {
        return "Employee{name = " + name + ", salary = " + salary + "}";
    }
}
class Coder extends Employee {
    @Override
    public void work() {
        System.out.println("程序员写代码...");
    }
}
class Manager extends Employee {
    @Override
    public void work() {
        System.out.println("项目经理分配任务...");
    }
}
补充知识-树
基本概念

| 概念 | 理解 | 
|---|---|
| 节点(其他叫法:结点、Node) | 上边的每一个圈圈都是一个节点; (节点内部存储:父节点地址、节点值、左子节点地址、右子节点地址) | 
| 度 | 每一个节点的子节点数量 (二叉数中,任意节点的度<=2) | 
| 树高 | 整棵树的层数 (上边数的树高=4) | 
| 根节点 | 最顶层的节点(22) | 
| 左子节点 | 22的左子节点是18 | 
| 右子节点 | 22的右子节点是26 | 
| 根节点的左子树 | 18节点及其所有子节点 | 
| 根节点的右子树 | 26节点及其所有子节点 | 
二叉树-普通二叉树
无排序

二叉树-二叉查找树
有排序
又名:二叉排序树、二叉搜索树

-  每个节点最多两个子节点 
-  任意节点左子树上的值都小于当前节点 
-  任意节点右子树的值都大于当前节点 
添加节点流程
-  小的节点存左边 
-  大的节点存右边 
-  一样的节点不存 
优点、不足
-  优点:元素查找速度快,每一次查找,筛选掉剩余元素的一半 
-  不足:特殊二叉查找树(所有节点仅有右节点或左节点),每次查找并不能过滤掉一半元素,查找速度跟数组一样 
二叉树-平衡二叉树
任意节点左右子树高度差不超过1
旋转机制
- 挺巧妙地,用到了再说
二叉树-红黑树
- 不是通过高度来平衡的,而是通过红黑规则来平衡的
红黑规则
- 用到再说
补充知识-常见数据结构
| 数据结构 | 结构 | 操作 | 特点 | 补充 | 
|---|---|---|---|---|
| 栈 | 一端开口(栈顶)、一端封闭(栈底) | 进栈/压栈、出栈/弹栈 | 后进先出,先进后出 | |
| 队列 | 一端开口(后端)、一端开口(前端) | 入队列(后端)、出队列(前端) | 先进先出,后进后出 | |
| 数组 | 地址值、索引 | 根据地址值和索引定位数据 | 查询速度快(且一致):索引+地址值定位; 增、删效率低:增删过程大概率伴随大量数据移动 | |
| 链表 | 见下图 | 查询慢:查询任何数据都要从头开始查 增删相对快:查到对应元素,更改节点存储内容 | 存储内存不连续 | |
| 双向链表 | 见下图 | 同【链表】 | 存储内存不连续 | |
| 树 | 见【补充知识-树】 | 
栈

队列

链表

单项链表和双向链表

集合底层数据结构
Collection-List集合
| ArrayList | LinkedList | 
|---|---|
| 数组 | 双向链表 | 
Collection-Set集合
Set的底层就是Map
| TreeSet | HashSet | LinkedHashSet | 
|---|---|---|
| 红黑树 | 哈希表(数组+链表+红黑树) | 哈希表+双向链表 | 
Map集合
| TreeMap | HashMap | LinkedHashMap | 
|---|---|---|
| 红黑树 | 哈希表(数组+链表+红黑树) | 哈希表+双向链表 | 
集合
是一种容器,用来装数据,类似数组,但长度可变
集合分类
大分类
| 单列集合 | 双列集合 | |
|---|---|---|
| 区别 | 一次添加一个元素 | 一次添加两个元素 | 
| 成员 | ArrayList、LinkedList、TreeSet、HashSet、LinkedHashSet | TreeMap、HashMap、LinkedHashMap | 
| 接口实现 | 实现Collection接口 | 实现Map接口 | 
小分类
Collection接口的子类
| List接口 | Set接口 | |
|---|---|---|
| 成员 | ArrayList、LinkedList | TreeSet、HashSet、LinkedHashSet | 
| 特点 | 存取有序、有索引、可以重复存储 | 存取无序、没有索引、不可以重复存储 | 
- TreeSet可以实现排序,按照排序规则输出元素
- HashSet保证元素唯一
- LinkedHashSet可以保证存储顺序
Map接口的子类
| Map接口 | |
|---|---|
| 成员 | TreeMap、HashMap、LinkedHashMap | 
| 特点 | 同Set接口 | 
集合的遍历方式
通用遍历方式
迭代器
增强for循环
forEach方法
package com.itheima.collection;
import com.itheima.domain.Student;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest2 {
    public static void main(String[] args) {
        Collection<Student> c = new ArrayList<>();
        c.add(new Student("张三", 23));
        c.add(new Student("李四", 24));
        c.add(new Student("王五", 25));
        // 1. 获取迭代器
        Iterator<Student> it = c.iterator();
        // 2. 循环判断, 集合中是否还有元素
        while (it.hasNext()) {
            // 3. 调用next方法, 将元素取出
            Student stu = it.next();
            System.out.println(stu.getName() + "---" + stu.getAge());
        }
        System.out.println("--------------------");
        // 使用增强for循环遍历集合:内部还是迭代器,通过.class文件可以看出来
        for (Student stu : c) {
            System.out.println(stu);
        }
        System.out.println("--------------------");
        // foreach方法遍历集合:匿名内部类
        c.forEach(stu -> System.out.println(stu));
    }
}
额外遍历方式-List集合
普通for循环
ListIterator(List集合特有的迭代器)
package com.itheima.collection.list;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListDemo2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("abc");
        list.add("bbb");
        list.add("ccc");
        list.add("abc");
        // 普通for循环
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
        System.out.println("---------------");
        // 迭代器
        // 先调用hasPrevious会打印不出来数据
        /// 需要先调用hasNext,再掉用hasPrevious,才能保证一次正向打印,一次反向打印
        ListIterator<String> it = list.listIterator();
        while(it.hasPrevious()){
            String s = it.previous();
            System.out.println(s);
        }
        System.out.println("---------------");
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
    }
}
ListIterator(List集合特有的迭代器)遍历过程中的添加、删除元素注意事项
用迭代器遍历过程中,调用【集合对象】的添加、删除操作,就会出现异常
用【迭代器】对象的添加或删除方法,即可避免异常
- 普通迭代器:Iterator,仅支持删除操作,不支持添加操作
- List迭代器:ListIterator,既支持删除操作,也支持添加操作
package com.itheima.collection.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("眼瞅着你不是真正的高兴");
        list.add("温油");
        list.add("离开俺们这旮表面");
        list.add("伤心的人别扭秧歌");
        list.add("私奔到东北");
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()) {
            String s = it.next();
            if ("温油".equals(s)) {
                it.add("哈哈");  // 没问题
                list.add("哈哈"); // 报错
                list.remove("温油"); // 报错
            }
        }
        System.out.println(list);
    }
}
Collection接口–通用函数

-  remove()、contains()两个函数依赖对象底层的equals()方法 
-  若对象没有重写equals()方法,比较的是地址值,无法按照要求删除 
-  上述方法均以对象为参数,不以索引为参数(因为Collection接口里面的Set接口没有索引) 
Collection接口–List接口-ArrayList
支持索引
增、删、改、查(索引)
package com.itheima.collection.list;
import java.util.ArrayList;
import java.util.List;
public class ListDemo1 {
    /*
        List接口的特点 : 存取有序, 有索引, 可以存储重复的
        和“索引”有关的API :
            public void add(int index, E element) : 在指定的索引位置, 添加元素
            public E remove(int index) : 根据索引删除集合中的元素
            public E set(int index, E element) : 根据索引修改集合中的元素
            public E get(int index) : 返回指定索引处的元素
     */
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.set(0, "赵六");
        list.remove(1);
        System.out.println(list.get(0));
        System.out.println(list);
        System.out.println("-------------------------");
        List<Integer> list2 = new ArrayList<>();
        list2.add(111);        // Integer e = 111;
        list2.add(222);
        list2.add(333);
        list2.remove(Integer.valueOf(222));
        System.out.println(list2);
    }
}
-  根据索引删除元素是List接口(子)的特点 
-  根据元素删除元素是Collection接口(父)的特点 
ArrayList长度可变原理
- 空参构造时,底层创建长度为0的数组(非空参构造,创建长度为10的数组)
- 添加第一个元素时,底层会创建一个新的长度为10的数组
- 当数组存满时,会扩容1.5倍
Collection接口–List接口-LinkedList
支持索引
增、删、改、查、查首个、查尾部(非索引)
package com.itheima.collection.list;
import java.util.LinkedList;
public class LinkedListDemo {
    /*
        LinkedList 特有方法 :
            public void addFirst(E e) : 头部添加
            public void addLast(E e) : 尾部添加
            public E getFirst() : 获取第一个
            public E getLast() : 获取最后一个
            public E removeFirst() : 删除第一个
            public E removeLast() : 删除最后一个
     */
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        String s = list.get(1);
        System.out.println(s);
    }
    private static void method2() {
        LinkedList<String> list = new LinkedList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        System.out.println(list.getFirst());
        System.out.println(list.getLast());
        list.removeFirst();
        list.removeLast();
        System.out.println(list);
    }
    private static void method1() {
        LinkedList<String> list = new LinkedList<>();
        list.addFirst("张三");
        list.addFirst("李四");
        list.addFirst("王五");
        list.addLast("赵六");
        // 王五 李四 张三 赵六
        System.out.println(list);
    }
}
LinkedList也归属与List接口,具有get(int index)方法,但是其底层是双向链表,get方法怎么实现?
- 将索引和集合长度比较
- 靠近头部:从头找数据,一个一个找
- 靠近尾部:从尾找数据,一个一个找
不同于ArrayList的查询(地址值+索引)。LinkedList的存储内存不连续,是一个一个元素查找,速度慢
Collection接口–Set接口-TreeSet
对集合中的元素进行排序操作(底层红黑树实现)
两种排序规则-自然排序
常见类排序:String,默认增序排序
package com.itheima.day10.set;
import java.util.TreeSet;
public class TreeSetDemo1 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>();
        ts.add("a");
        ts.add("d");
        ts.add("e");
        ts.add("c");
        ts.add("b");
        ts.add("b");
        ts.add("b");
        ts.add("b");
        ts.add("b");
        System.out.println(ts);  // [a, b, c, d, e]
    }
}
自定义类排序
-  类实现Comparable接口 
-  重写compareTo方法 
-  根据返回值,组织排序规则 -  返回负数:往树左边走 
-  返回正数:往树右边走 
-  返回0:不存(对于根节点除外,即第一个元素除外) 
 
-  
package com.itheima.day10.set;
import com.itheima.day10.domain.Student;
import java.util.TreeSet;
public class TreeSetDemo2 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();
        ts.add(new Student("A", 23));
        ts.add(new Student("B", 26));
        ts.add(new Student("C", 27));
        ts.add(new Student("D", 20));
        // [Student{name = C, age = 27}, Student{name = B, age = 26}, Student{name = A, age = 23}, Student{name = D, age = 20}]
        System.out.println(ts);
    }
}
比较过程:
23+A—23+A
26+B—23+A
27+C—23+A
27+C—26+B
20+D—26+B
20+D—23+A
// Student类
package com.itheima.day10.domain;
public class Student implements Comparable<Student>{
    // this.xxx - o.xxx  正序
    // o.xxx - this.xxx  降序
    @Override
    public int compareTo(Student o) {
        System.out.println(this.getAge() + "+" + this.name + "---" + o.age + "+" + o.name);
        // 根据年龄做主要排序条件
        int ageResult = o.age - this.age;
        // 根据姓名做次要排序条件
        int nameResult = ageResult == 0 ? o.name.compareTo(this.name) : ageResult;
        // 判断姓名是否相同
        int result = nameResult == 0 ? 1 : nameResult;
        return result;
    }
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
两种排序规则-比较器排序
-  在 TreeSet 的构造方法中, 传入 Compartor 接口的实现类对象 
-  重写 compare 方法 
-  根据方法的返回值, 来组织排序规则 -  负数 : 左边走 
-  正数 : 右边走 
-  0 : 不存 
 
-  
该接口也是函数式接口,可以写为Lambda表达式
package com.itheima.day10.set;
import com.itheima.day10.domain.Student;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo3 {  
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int ageResult = o1.getAge() - o2.getAge();
                return ageResult == 0 ? o1.getName().compareTo(o2.getName()) : ageResult;
            }
        });
        ts.add(new Student("赵六", 26));
        ts.add(new Student("李四", 24));
        ts.add(new Student("张三", 23));
        ts.add(new Student("王五", 25));
		
        // [Student{name = 张三, age = 23}, Student{name = 李四, age = 24}, Student{name = 王五, age = 25}, Student{name = 赵六, age = 26}]
        System.out.println(ts);
    }
}
两种排序规则优先级
如果同时具备比较器和自然排序, 会优先按照比较器的规则, 进行排序操作
- 有些类给出默认的排序规则,不好重写其底层代码,就用比较器
Collection接口–Set接口-HashSet
可以保证数据唯一性
版本信息
| JDK7 | JDK8 | 
|---|---|
| 数组+链表 | 数组+链表+红黑树 | 
数据存储过程
- 每个数据的存储都需要进行比较,根据比较结果来判断是否存
- 需要重写存入对象的hashCode方法、equals方法
- 创建一个默认长度16的数组,数组名table
- 根据元素的哈希值跟数组的长度求余计算出应存入的位置(调用hashCode方法)
- 判断当前位置是否为null,如果是null直接存入
- 如果位置不为null,表示有元素,则调用equals方法比较
- 如果一样,则不存,如果不一样,则存入数组 
  - JDK 7新元素占老元素位置,指向老元素 (头插法)
- JDK 8中新元素挂在老元素下面(尾插法)
 

数组扩容
- 条件1:数组存满到16*0.75(加载因子)=12时,就自动扩容,每次扩容为原先的两倍
- 条件2:链表挂载元素超过了8个 (阈值) ,但数组长度没有到达64
链表转红黑树
- 条件:链表挂载元素超过了8个 (阈值),且数组长度到达64
特点
-  底层是哈希表存储数据,对增删改查数据性能都比较好 
-  存储的数据具有唯一性 
-  不对数据进行排序 
Collection接口–Set接口-LinkedHashSet

Collection接口–总结
类型选用
| Case | Choice | 
|---|---|
| 如果想要集合中的元素可重复 | ArrayList | 
| 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询 | LinkedList | 
| 如果想对集合中的元素去重 | HashSet | 
| 如果想对集合中的元素去重,而且保证存取顺序 | LinkedHashSet | 
| 如果想对集合中的元素进行排序 | TreeSet | 
Collections工具类

package com.itheima.day11.tools;
import com.itheima.day11.domain.Student;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class CollectionsDemo {
    public static void main(String[] args) {
        // 批量添加
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c", "d");
        System.out.println(list);
        // 二分查找 (前提: 必须是排好序的数据)
        System.out.println(Collections.binarySearch(list, "b"));
        // 洗牌
        Collections.shuffle(list);
        System.out.println(list);
        ArrayList<Student> nums = new ArrayList<>();
        Collections.addAll(nums, new Student("张三", 23), new Student("王五", 25), new Student("李四", 24));
        // 从集合中找最值
        System.out.println(Collections.max(nums));
        System.out.println(Collections.min(nums));
        // 对集合中的元素进行交换
        Collections.swap(nums, 0, 2);
        System.out.println(nums);
        // sort : 对集合进行排序
        ArrayList<Integer> box = new ArrayList<>();
        Collections.addAll(box, 1, 3, 5, 2, 4);
        Collections.sort(box, (o1, o2) -> o2 - o1);
        System.out.println(box);
    }
}
Map接口
基础介绍
- Map是双列集合,每个元素包含两个数据
- Map元素格式:key=value 
  - key:不允许重复
- value:允许重复
- key-value一一对应
 
- key-value整体,称为【键值对】或【键值对对象】,用Entry表示
Map类别
TreeMap、HashMap、LinkedHashMap
通用方法
Map是双列集合的顶层接口

底层原理-HashMap举例

- 用键值对中的键来计算哈希值,与值无关
- 后续原理同HashSet
遍历方式
通过forEach方法遍历
通过键值对对象获取键和值
通过键找值
package com.itheima.day11.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
public class MapDemo3 {
    public static void main(String[] args) {
        HashMap<String, String> hm = new HashMap<>();
        hm.put("张三", "北京");
        hm.put("李四", "上海");
        hm.put("王五", "成都");
        hm.forEach((key, value) -> System.out.println(key + "---" + value));
    }
    private static void method2(HashMap<String, String> hm) {
        // 1. 获取到所有的键值对对象
        Set<Map.Entry<String, String>> entrySet = hm.entrySet();
        // 2. 遍历set集合获取每一个键值对对象
        for (Map.Entry<String, String> entry : entrySet) {
            // 3. 通过键值对对象, 获取键和值
            System.out.println(entry.getKey() + "---" + entry.getValue());
        }
    }
    private static void method1(HashMap<String, String> hm) {
        // 1. 获取到所有的键
        Set<String> keySet = hm.keySet();
        // 2. 遍历set集合, 获取每一个键
        for (String key : keySet) {
            // 3. 调用map集合的get方法, 根据键查找对应的值
            String value = hm.get(key);
            System.out.println(key + "---" + value);
        }
    }
}
Map集合选用
| Case | Choice | 
|---|---|
| 如果想根据集合中的键进行去重 | HashMap | 
| 如果想根据键对集合中的元素去重,而且保证存取顺序 | LinkedHashMap | 
| 如果想根据集合中的键进行排序 | TreeMap | 



















