1.Collection集合

1.1 数组和集合的区别【理解】

  • 相同点
    都是容器,可以存储多个数据
  • 不同点
    • 数组的长度是不可变的,集合的长度是可变的
    • 数组可以存基本数据类型和引用数据类型
      集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类

1.2 集合类体系结构【理解】

01_集合类体系结构图.png

1.3 Collection 集合概述和使用【应用】

  • Collection集合概述
    • 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
    • JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
  • 创建Collection集合的对象
    • 多态的方式
    • 具体的实现类ArrayList
  • Collection集合常用方法 | 方法名 | 说明 | | :—- | :—- | | boolean add(E e) | 添加元素 | | boolean remove(Object o) | 从集合中移除指定的元素 | | boolean removeIf(Object o) | 根据条件进行移除 | | void clear() | 清空集合中的元素 | | boolean contains(Object o) | 判断集合中是否存在指定的元素 | | boolean isEmpty() | 判断集合是否为空 | | int size() | 集合的长度,也就是集合中元素的个数 |

1.4 Collection集合的遍历【应用】

  • 迭代器介绍
    • 迭代器,集合的专用遍历方式
    • Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
  • Iterator中的常用方法
    boolean hasNext(): 判断当前位置是否有元素可以被取出
    E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
  • Collection集合的遍历

    1. public class IteratorDemo1 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Collection<String> c = new ArrayList<>();
    5. //添加元素
    6. c.add("hello");
    7. c.add("world");
    8. c.add("java");
    9. c.add("javaee");
    10. //返回此集合中元素的迭代器,通过集合的iterator()方法得到
    11. Iterator<String> it = c.iterator();
    12. //用while循环改进元素的判断和获取
    13. while (it.hasNext()) {
    14. String s = it.next();
    15. System.out.println(s);
    16. }
    17. }
    18. }

1.5 Collection迭代器原理分析

2020-07-13_075716.png

  • Iterator iterator(): 获取迭代器对象,默认指向0索引
  • boolean hasNext():判断当前位置是否有元素可以被取出
  • E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置

1.6 Collection迭代器中删除的方法

循环遍历删除集合中的元素方式

  • 方式1:采用Collection的remove()方法删除元素,相邻重复元素删除要注意索引问题。
    • 每次删除后索引会重新排序
  • 方式2:建议,通过迭代器对象删除集合中的元素
    • void remove(): 删除迭代器对象当前指向的元素
  1. public class IteratorDemo2 {
  2. public static void main(String[] args) {
  3. ArrayList<String> list = new ArrayList<>();
  4. list.add("a");
  5. list.add("b");
  6. list.add("b");
  7. list.add("c");
  8. list.add("d");
  9. Iterator<String> it = list.iterator();
  10. while(it.hasNext()){
  11. String s = it.next();
  12. if("b".equals(s)){
  13. //指向谁,那么此时就删除谁.
  14. it.remove();
  15. }
  16. }
  17. System.out.println(list);
  18. }
  19. }

1.7 增强for循环【应用】

  • 介绍
    • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
    • 实现Iterable接口的类才可以使用迭代器和增强for
    • 简化数组和Collection集合的遍历
  • 格式 ```java for(集合/数组中元素的数据类型 变量名 : 集合/数组名) { // 已经将当前遍历到的元素封装到变量中了,直接使用变量即可

}

  1. - 代码
  2. ```java
  3. public class MyCollectonDemo1 {
  4. public static void main(String[] args) {
  5. ArrayList<String> list = new ArrayList<>();
  6. list.add("a");
  7. list.add("b");
  8. list.add("c");
  9. list.add("d");
  10. list.add("e");
  11. list.add("f");
  12. //1,数据类型一定是集合或者数组中元素的类型
  13. //2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素
  14. //3,list就是要遍历的集合或者数组
  15. for(String str : list){
  16. System.out.println(str);
  17. }
  18. }
  19. }

1.8 增强for循环注意点

  • 增强for中,修改接收集合元素变量的值,不会改变集合
  • 三种遍历方式使用场景
    • 普通for:遍历过程中需要增删元素或操作索引时使用
    • 迭代器:遍历过程中需要删除元素时使用
    • 增强for:如果仅仅想遍历,那么使用增强for,若要增删元素则不能使用

扩展面试题:并发修改异常

  1. /**
  2. * 并发修改异常
  3. */
  4. public class Demo06 {
  5. public static void main(String[] args) {
  6. ArrayList<String> list = new ArrayList<>();
  7. list.add("a");
  8. list.add("b");
  9. list.add("c");
  10. list.add("d");
  11. list.add("d");
  12. //循环过程中可以随意删除和增加元素
  13. // for (int i = 0; i < list.size(); i++) {
  14. // String s = list.get(i);
  15. // if("c".equals(s)) {
  16. // list.add("e");
  17. // }
  18. // }
  19. // System.out.println(list);
  20. //循环过程中,不可以使用集合的增加和删除元素的方法
  21. //可以使用迭代器的删除方法
  22. Iterator<String> iterator = list.iterator();
  23. while (iterator.hasNext()) {
  24. String s = iterator.next();
  25. if("c".equals(s)) {
  26. list.add("c");//modCount = 6
  27. }
  28. }
  29. System.out.println(list);
  30. //循环过程中,不能增加和删除元素
  31. for (String s : list) {
  32. list.remove("c");
  33. }
  34. }
  35. }

ArrList—->modCount=0

add() modCount++

Iterator() —> new Itr ——>expectedModCount = modCount

while() {

  1. it.next() --- > expectedModCount == modCount
  2. add() modCount++

}

2.List集合

2.1 List集合的概述和特点【记忆】

  • List集合的概述
    • 有序集合,这里的有序指的是存取顺序
    • 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
    • 与Set集合不同,列表通常允许重复的元素
  • List集合的特点
    • 存取有序
    • 可以重复
    • 有索引

2.2 List集合的特有方法【应用】

  • 方法介绍 | 方法名 | 描述 | | —- | —- | | void add(int index,E element) | 在此集合中的指定位置插入指定的元素 | | E remove(int index) | 删除指定索引处的元素,返回被删除的元素 | | E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 | | E get(int index) | 返回指定索引处的元素 |

  • 示例代码

    1. /**
    2. * List的方法
    3. */
    4. public class Demo02 {
    5. public static void main(String[] args) {
    6. List<String> list = new ArrayList<>();
    7. list.add("aaa");
    8. list.add("bbb");
    9. list.add("ccc");
    10. //method1(list);
    11. //method2(list);
    12. //method3(list);
    13. //method4(list);
    14. }
    15. private static void method4(List<String> list) {
    16. //返回指定索引处的元素
    17. String s = list.get(0);
    18. System.out.println(s);
    19. }
    20. private static void method3(List<String> list) {
    21. //修改指定索引处的元素,返回被修改的元素
    22. String result = list.set(0, "qqq");
    23. System.out.println(result);
    24. System.out.println(list);
    25. }
    26. private static void method2(List<String> list) {
    27. //删除指定索引处的元素,返回被删除的元素
    28. String s = list.remove(0);
    29. System.out.println(s);
    30. System.out.println(list);
    31. }
    32. private static void method1(List<String> list) {
    33. //在此集合中的指定位置插入指定的元素
    34. list.add(0,"qqq");
    35. System.out.println(list);
    36. }

3.数据结构

3.1 数据结构之栈和队列【记忆】

  • 栈结构
    ,只有一个开口,先进去的就到最底下,后进来的就在前面,要是拿出去的话,肯定是从开口端拿出去,所以说先进后出,后进先出。
    常用词—压栈、弹栈

1341698-20180816202825286-1253903743.jpg

  • 队列结构
    队列有队头(front)和队尾(end),数据从队尾进入队列,从队头出队列,队头(front)指向队列的第一个数据,队尾(end)指向队列中的最后一个数据

1341698-20180816204430915-1717851170.jpg

3.2 数据结构之数组和链表【记忆】

  • 数组结构
    数组就像身上编了号站成一排的人,要找第10个人很容易,根据人身上的编号很快就能找到。但插入、删除慢,要向某个位置插入或删除一个人时,后面的人身上的编号都要变。当然,加入或删除的人始终末尾的也快。
    查询快,增删慢

1078769-20170916235630297-297624729.png

  • 链表结构
    链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素。
    查询慢、增删快
    • 单向链表
      单向链表包含两个域,一个是信息域,一个是指针域。也就是单向链表的节点被分成两部分,一部分是保存或显示关于节点的信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值。

20150303094256114.jpg

  • 双向链表
    每个节点有2个链接,一个是指向前一个节点(当此链接为第一个链接时,指向的是空值或空列表),另一个则指向后一个节点(当此链接为最后一个链接时,指向的是空值或空列表)。意思就是说双向链表有2个指针,一个是指向前一个节点的指针,另一个则指向后一个节点的指针。

20150303101318960.jpg

  • 循环链表
    循环链表就是首节点和末节点被连接在一起。循环链表中第一个节点之前就是最后一个节点,反之亦然

20150303103205580.jpg

4.List集合的实现类

4.1 List集合子类的特点【记忆】

  • ArrayList源码解析

ArrayList第一次添加元素的源码解析.png

  • ArrayList底层是基于数组
  • 数组,第一次初始化长度为0——第一次扩容10
  • 以后每次扩容,数组的1.5倍

4.2 LinkedList基本运用

  1. /**
  2. * LinkedList基本使用
  3. */
  4. public class Demo03 {
  5. public static void main(String[] args) {
  6. LinkedList<String> list = new LinkedList<>();
  7. list.add("aa");
  8. list.add("aa");
  9. list.add("bb");
  10. list.add("cc");
  11. list.add("dd");
  12. for (int i = 0; i < list.size(); i++) {
  13. System.out.println(list.get(i));
  14. }
  15. System.out.println("----------------------");
  16. Iterator<String> it = list.iterator();
  17. while (it.hasNext()) {
  18. String s = it.next();
  19. System.out.println(s);
  20. }
  21. System.out.println("----------------------");
  22. for (String s : list) {
  23. System.out.println(s);
  24. }
  25. System.out.println("-----------------------");
  26. }
  27. }

4.2 LinkedList集合的特有功能【应用】

  • 特有方法 | 方法名 | 说明 | | —- | —- | | public void addFirst(E e) | 在该列表开头插入指定的元素 | | public void addLast(E e) | 将指定的元素追加到此列表的末尾 | | public E getFirst() | 返回此列表中的第一个元素 | | public E getLast() | 返回此列表中的最后一个元素 | | public E removeFirst() | 从此列表中删除并返回第一个元素 | | public E removeLast() | 从此列表中删除并返回最后一个元素 |

  • 示例代码

    1. public class MyLinkedListDemo4 {
    2. public static void main(String[] args) {
    3. LinkedList<String> list = new LinkedList<>();
    4. list.add("aaa");
    5. list.add("bbb");
    6. list.add("ccc");
    7. //在该列表开头插入指定的元素
    8. //method1(list);
    9. //将指定的元素追加到此列表的末尾
    10. //method2(list);
    11. //返回此列表中的第一个元素
    12. //返回此列表中的最后一个元素
    13. //method3(list);
    14. //从此列表中删除并返回第一个元素
    15. //从此列表中删除并返回最后一个元素
    16. //method4(list);
    17. }
    18. private static void method4(LinkedList<String> list) {
    19. String first = list.removeFirst();
    20. System.out.println(first);
    21. String last = list.removeLast();
    22. System.out.println(last);
    23. System.out.println(list);
    24. }
    25. private static void method3(LinkedList<String> list) {
    26. String first = list.getFirst();
    27. String last = list.getLast();
    28. System.out.println(first);
    29. System.out.println(last);
    30. }
    31. private static void method2(LinkedList<String> list) {
    32. list.addLast("www");
    33. System.out.println(list);
    34. }
    35. private static void method1(LinkedList<String> list) {
    36. list.addFirst("qqq");
    37. System.out.println(list);
    38. }
    39. }

4.3 LinkedList源码解析

2020-07-14_082506.png

LinkedList添加元素源码解析.png

5.泛型

5.1 泛型概述【理解】

  • 泛型的介绍
    泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制
  • 泛型的好处
    • 把运行时期的问题提前到了编译期间
    • 避免了强制类型转换
  • 泛型的定义格式
    • <类型>: 指定一种类型的格式,尖括号里面可以任意书写,一般只写一个字母.例如:
    • <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如:
  • 泛型,本质就是类型参数化。

5.2 泛型类的使用【应用】

  • 如果一个类的后面有,表示这个类是一个泛型类
  • 创建泛型类的对象时,必须要给这个泛型确定具体的数据类型

5.3 自定义泛型类【应用】

  • 格式: 修饰符 class 类名<类型> { }
    范例: public class Generic { }
    常见的如:T、E、K、V等形式的参数常用于表示泛型
  • 示例代码

    • 泛型类

      1. public class Box<T> {
      2. private T element;
      3. public T getElement() {
      4. return element;
      5. }
      6. public void setElement(T element) {
      7. this.element = element;
      8. }
      9. }
    • 测试类

      1. /**
      2. * 自定义泛型类
      3. */
      4. public class Demo01 {
      5. public static void main(String[] args) {
      6. Box<String> box1 = new Box<>();
      7. box1.setElement("香蕉");
      8. System.out.println(box1.getElement());
      9. Box<Integer> box2 = new Box<>();
      10. box2.setElement(100);
      11. System.out.println(box2.getElement());
      12. }
      13. }

5.4 泛型方法的使用【应用】

  • 泛型方法的使用
    • 在调用方法时,确定泛型的具体类型
  • 示例代码

    1. /**
    2. * 泛型方法的使用
    3. */
    4. public class Demo02 {
    5. public static void main(String[] args) {
    6. ArrayList<String> list = new ArrayList<>();
    7. list.add("a");
    8. list.add("b");
    9. list.add("c");
    10. list.add("d");
    11. Object[] objects = list.toArray();
    12. System.out.println(Arrays.toString(objects));
    13. System.out.println("-----------------");
    14. //泛型方法
    15. String[] strings = list.toArray(new String[list.size()]);
    16. System.out.println(Arrays.toString(strings));
    17. }
    18. }

5.5 自定义泛型方法【应用】

  • 定义格式

    1. 修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
  • 范例: public void show(T t) { }

  • 示例代码 ```java /**

    • 自定义泛型方法 */ public class Demo03 { public static void main(String[] args) {

      1. ArrayList<String> list1 = addElement(new ArrayList<String>(), "a", "b", "c", "d");
      2. System.out.println(list1);
      3. ArrayList<Integer> list2 = addElement(new ArrayList<Integer>(), 1, 2, 3, 4);
      4. System.out.println(list2);
  1. }
  2. public static <T> ArrayList<T> addElement(ArrayList<T> list, T t1,T t2,T t3,T t4) {
  3. list.add(t1);
  4. list.add(t2);
  5. list.add(t3);
  6. list.add(t4);
  7. return list;
  8. }
  1. <a name="65787ace"></a>
  2. ### 5.6 泛型接口【应用】
  3. - 定义格式
  4. ```java
  5. 修饰符 interface 接口名<类型> { }
  • 范例

    1. public interface Generic<T> {
    2. void show(T t);
    3. }
  • 泛型接口实现类1

    • 定义实现类时,定义和接口相同泛型,创建实现类对象时明确泛型的具体类型
      1. public class GenericImpl1<T> implements Generic<T> {
      2. @Override
      3. public void show(T t) {
      4. System.out.println(t);
      5. }
      6. }
  • 泛型接口实现类2

    • 定义实现类时,直接明确泛型的具体类型
      1. public class GenericImpl2 implements Generic<Integer>{
      2. @Override
      3. public void show(Integer t) {
      4. System.out.println(t);
      5. }
      6. }
  • 测试类

    1. public class GenericDemo3 {
    2. public static void main(String[] args) {
    3. GenericImpl1<String> g1 = new GenericImpl<String>();
    4. g1.show("林青霞");
    5. GenericImpl1<Integer> g2 = new GenericImpl<Integer>();
    6. g2.show(30);
    7. GenericImpl2 g3 = new GenericImpl2();
    8. g3.show(10);
    9. }
    10. }

5.7 类型通配符

  • 类型通配符: <?>
    • ArrayList<?>:表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
    • 但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
  • 类型通配符上限: <? extends 类型>
    • ArrayListList <? extends Number>:它表示的类型是Number或者其子类型
  • 类型通配符下限: <? super 类型>
    • ArrayListList <? super Number>: 它表示的类型是Number或者其父类型
  • 泛型通配符的使用

    1. public class GenericDemo4 {
    2. public static void main(String[] args) {
    3. ArrayList<Integer> list1 = new ArrayList<>();
    4. ArrayList<String> list2 = new ArrayList<>();
    5. ArrayList<Number> list3 = new ArrayList<>();
    6. ArrayList<Object> list4 = new ArrayList<>();
    7. method(list1);
    8. method(list2);
    9. method(list3);
    10. method(list4);
    11. getElement1(list1);
    12. getElement1(list2);//报错
    13. getElement1(list3);
    14. getElement1(list4);//报错
    15. getElement2(list1);//报错
    16. getElement2(list2);//报错
    17. getElement2(list3);
    18. getElement2(list4);
    19. }
    20. // 泛型通配符: 此时的泛型?,可以是任意类型
    21. public static void method(ArrayList<?> list){}
    22. // 泛型的上限: 此时的泛型?,必须是Number类型或者Number类型的子类
    23. public static void getElement1(ArrayList<? extends Number> list){}
    24. // 泛型的下限: 此时的泛型?,必须是Number类型或者Number类型的父类
    25. public static void getElement2(ArrayList<? super Number> list){}
    26. }

6.Set集合

01_集合类体系结构图.png

6.1 Set集合概述和特点【应用】

  • 不可以存储重复元素
  • 存取顺序不一致
  • 没有索引,不能使用带索引的方法,不能使用普通for循环遍历

6.2 Set集合的基本使用【应用】

存储字符串并遍历

  1. public class MySet1 {
  2. public static void main(String[] args) {
  3. //创建集合对象
  4. Set<String> set = new TreeSet<>();
  5. //添加元素
  6. set.add("ccc");
  7. set.add("aaa");
  8. set.add("aaa");
  9. set.add("bbb");
  10. //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
  11. //遍历集合
  12. Iterator<String> it = set.iterator();
  13. while (it.hasNext()){
  14. String s = it.next();
  15. System.out.println(s);
  16. }
  17. System.out.println("-----------------------------------");
  18. for (String s : set) {
  19. System.out.println(s);
  20. }
  21. }
  22. }

7.TreeSet集合

7.1 TreeSet集合概述和特点【应用】

  • 不可以存储重复元素
  • 没有索引
  • 可以将元素按照规则进行排序
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator) :根据指定的比较器进行排序

7.2 TreeSet集合基本使用【应用】

存储Integer类型的整数并遍历

  1. public class TreeSetDemo01 {
  2. public static void main(String[] args) {
  3. //创建集合对象
  4. TreeSet<Integer> ts = new TreeSet<Integer>();
  5. //添加元素
  6. ts.add(10);
  7. ts.add(40);
  8. ts.add(30);
  9. ts.add(50);
  10. ts.add(20);
  11. ts.add(30);
  12. //遍历集合
  13. for(Integer i : ts) {
  14. System.out.println(i);
  15. }
  16. }
  17. }

7.3 TreeSet-自然排序【应用】

  • 自然排序简单原理 Comparable compareTo

    • 如果返回值为负数,标识当前存入的元素是较小值,存左边
    • 如果返回值为0,表式当前存入的元素跟集合中元素重复了,不存
    • 如果返回值为正数,表式当前存入的元素是较大值,存右边
  • 案例需求

    • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
    • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 实现步骤
    1. 使用空参构造创建TreeSet集合
      • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
    2. 自定义的Student类实现Comparable接口
      • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
    3. 重写接口中的compareTo方法
      • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
        • this – o : 升序
        • o – this : 降序
  • 代码实现
    学生类

    1. public class Student implements Comparable<Student>{
    2. private String name;
    3. private int age;
    4. public Student() {
    5. }
    6. public Student(String name, int age) {
    7. this.name = name;
    8. this.age = age;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public int getAge() {
    17. return age;
    18. }
    19. public void setAge(int age) {
    20. this.age = age;
    21. }
    22. @Override
    23. public String toString() {
    24. return "Student{" +
    25. "name='" + name + '\'' +
    26. ", age=" + age +
    27. '}';
    28. }
    29. @Override
    30. public int compareTo(Student o) {
    31. //按照对象的年龄进行排序
    32. //主要判断条件: 按照年龄从小到大排序
    33. int result = this.age - o.age;
    34. //次要判断条件: 年龄相同时,按照姓名的字母顺序排序
    35. result = result == 0 ? this.name.compareTo(o.getName()) : result;
    36. return result;
    37. }
    38. }
  • 测试类

    1. public class MyTreeSet2 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. TreeSet<Student> ts = new TreeSet<>();
    5. //创建学生对象
    6. Student s1 = new Student("zhangsan",28);
    7. Student s2 = new Student("lisi",27);
    8. Student s3 = new Student("wangwu",29);
    9. Student s4 = new Student("zhaoliu",28);
    10. Student s5 = new Student("qianqi",30);
    11. //把学生添加到集合
    12. ts.add(s1);
    13. ts.add(s2);
    14. ts.add(s3);
    15. ts.add(s4);
    16. ts.add(s5);
    17. //遍历集合
    18. for (Student student : ts) {
    19. System.out.println(student);
    20. }
    21. }
    22. }

7.4 比较器排序Comparator的使用【应用】

  • TreeSet的带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

  • 案例需求

    • 存储老师对象并遍历,创建TreeSet集合使用带参构造方法
    • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 实现步骤
    • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
    • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
  • 代码实现
    老师类

    1. public class Teacher {
    2. private String name;
    3. private int age;
    4. public Teacher() {
    5. }
    6. public Teacher(String name, int age) {
    7. this.name = name;
    8. this.age = age;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public int getAge() {
    17. return age;
    18. }
    19. public void setAge(int age) {
    20. this.age = age;
    21. }
    22. @Override
    23. public String toString() {
    24. return "Teacher{" +
    25. "name='" + name + '\'' +
    26. ", age=" + age +
    27. '}';
    28. }
    29. }
  • 测试类

    1. public class MyTreeSet4 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
    5. @Override
    6. public int compare(Teacher o1, Teacher o2) {
    7. //o1表示现在要存入的那个元素
    8. //o2表示已经存入到集合中的元素
    9. //主要条件
    10. int result = o1.getAge() - o2.getAge();
    11. //次要条件
    12. result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
    13. return result;
    14. }
    15. });
    16. //创建老师对象
    17. Teacher t1 = new Teacher("zhangsan",23);
    18. Teacher t2 = new Teacher("lisi",22);
    19. Teacher t3 = new Teacher("wangwu",24);
    20. Teacher t4 = new Teacher("zhaoliu",24);
    21. //把老师添加到集合
    22. ts.add(t1);
    23. ts.add(t2);
    24. ts.add(t3);
    25. ts.add(t4);
    26. //遍历集合
    27. for (Teacher teacher : ts) {
    28. System.out.println(teacher);
    29. }
    30. }
    31. }

7.5 两种比较方式总结【理解】

  • 两种比较方式小结
    • 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
    • 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
    • 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
  • 两种方式中关于返回值的规则
    • 如果返回值为负数,表示当前存入的元素是较小值,存左边
    • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
    • 如果返回值为正数,表示当前存入的元素是较大值,存右边

8.数据结构

8.1 二叉树【理解】

  • 节点特点

2020-07-15_091155.png

  • 节点:在树结构中,每一个元素称之为节点
    • 一个节点没有父节点,那么父节点的地址为null
    • 一个节点没有子节点,那么左子节点和右子节点地址为null
  • 度:每一个节点的子节点数量称之为度
    • 二叉树的特点
  • 二叉树中,任意一个节点的度要小于等于2
    • 二叉树结构图
      01_二叉树结构图.png

8.2 二叉查找树【理解】

  • 二叉查找树的特点
    • 二叉查找树,又称二叉排序树或者二叉搜索树
      • 每一个节点上最多有两个子节点
      • 左子树上所有节点的值都小于根节点的值
      • 右子树上所有节点的值都大于根节点的值
      • 左、右子树也分别为二叉查找树
  • 二叉查找树结构图
    02_二叉查找树结构图.png
  • 二叉查找树和二叉树对比结构图03_二叉查找树和二叉树对比结构图.png

8.3 二叉查找树添加节点

  • 添加规则
    • 小的存左边
    • 大的存右边

04_二叉查找树添加节点规则.png

8.4 平衡二叉树【理解】

  • 平衡二叉树的特点
    • 二叉树左右两个子树的高度差不超过1
    • 任意节点的左右两个子树都是一颗平衡二叉树
  • 平衡二叉树示例
    2020-07-15_094842.png
    • 上面二图不是平衡二叉树的原因
      • 图一:10节点的左子树为高度为0,右子节点的高度为3
      • 图二:4节点的左子节点的高度为2,右子节点的高度为0

2020-07-15_094950.png

  • 小结:
    • 二叉树
      • 任意一个节点的度要小于等于2
    • 二叉查找树
      • 左子树上所有节点的值都小于根节点的值
      • 右子树上所有节点的值都大于根节点的值
    • 平衡二叉树
      • 二叉树左右两个子树的高度差不超过1

2020-07-15_100123.png

8.5 平衡二叉树-左旋 【理解】

  • 旋转类型
    • 左旋
    • 右旋
  • 旋转触发时机

    • 当添加一个节点之后,该树不再是一颗平衡二叉树(左右两个子树的高度差不超过1)
  • 左旋

    • 就是将根节点的右侧往左拉
    • 原先的右子节点变成新的父节点,并把自己多余的左子节点出让,给已经降级的根节点当右子节点

05_平衡二叉树左旋01.png

05_平衡二叉树左旋02.png

8.6 平衡二叉树-右旋 【理解】

  • 右旋
    • 就是将根节点的左侧往右拉
    • 左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节点当左子节点

06_平衡二叉树右旋01.png
**
06_平衡二叉树右旋02.png

8.7 平衡二叉树-小结 【理解】

  • 平衡二叉树左旋小结
    • 逆时针旋转
    • 右子节点变成父节点(根节点)
    • 原先的根节点降级变成左子节点
    • 将多余的左子节点出让,给降级的节点作为右子节点
  • 平衡二叉树右旋小结
    • 顺时针旋转
    • 左子节点变成父节点(根节点)
    • 原先的根节点降级变成右子节点
    • 将多余的右子节点出让,给降级的节点作为左子节点

8.8 平衡二叉树-左左和左右 【理解】

  • 左左
    • 左左:
      • 当根节点子树的子树有节点插入,导致二叉树不平衡
    • 如何旋转:直接对整体进行右旋即可
      08_平衡二叉树左左.png
  • 左右
    • 左右:
      • 当根节点子树的子树有节点插入,导致二叉树不平衡
    • 如何旋转: 先在左子树对应的节点位置进行左旋,在对整体进行右旋
      09_平衡二叉树左右.png

8.9 平衡二叉树-右右和右左 【理解】

  • 右右
    • 右右:
      • 当根节点子树的子树有节点插入,导致二叉树不平衡
    • 如何旋转: 直接对整体进行左旋即可
      10_平衡二叉树右右.png
  • 右左
    • 右左:
      • 当根节点子树的子树有节点插入,导致二叉树不平衡
    • 如何旋转: 先在右子树对应的节点位置进行右旋,在对整体进行左旋
      11_平衡二叉树右左.png

8.10 小结

2020-07-12_230749.png

  • 左左是什么意思? 如何旋转?
    左左: 当根节点左子树的左子树有节点插入,导致二叉树不平衡
    如何旋转: 直接对整体进行右旋即可
  • 左右是什么意思? 如何旋转?
    左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡
    如何旋转: 先在左子树对应的节点位置进行左旋,在对整体进行右旋
  • 右右是什么意思? 如何旋转?
    右右: 当根节点右子树的右子树有节点插入,导致二叉树不平衡
    如何旋转: 直接对整体进行左旋即可
  • 右左是什么意思? 如何旋转?
    右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡
    如何旋转: 先在右子树对应的节点位置进行右旋,在对整体进行左旋

9.红黑树

9.1 概述【理解】

  • 红黑树的特点
    • 平衡二叉B树
    • 每一个节点可以是红或者黑
    • 红黑树不是高度平衡的,它的平衡是通过”自己的红黑规则”进行实现的

12_红黑树结构图.png

9.2 红黑树-红黑规则

  • 红黑树的红黑规则有哪些
    1. 每一个节点或是红色的,或者是黑色的
    2. 根节点必须是黑色
    3. 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
    4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
    5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

9.3 红黑树-添加节点的默认颜色

  • 红黑树添加节点的默认颜色
    • 添加节点时,默认为红色,效率高13_红黑树添加节点颜色.png

9.4 红黑树-添加节点后如何保证红黑规则

  • 根节点位置
    • 直接变为黑色
  • 非根节点位置
    • 父节点为黑色
      • 不需要任何操作,默认红色即可
    • 父节点为红色
      • 叔叔节点为红色
        1. 将”父节点”设为黑色,将”叔叔节点”设为黑色
        2. 将”祖父节点”设为红色
        3. 如果”祖父节点”为根节点,则将根节点再次变成黑色
      • 叔叔节点为黑色
        1. 将”父节点”设为黑色
        2. 将”祖父节点”设为红色
        3. 以”祖父节点”为支点进行旋转

2020-07-15_112531.png

9.5 成绩排序案例【应用】

  • 案例需求
    • 用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩,英语成绩),并遍历该集合
    • 要求: 按照总分从高到低出现
  • 代码实现
    学生类

    1. public class Student implements Comparable<Student> {
    2. private String name;
    3. private int chinese;
    4. private int math;
    5. private int english;
    6. public Student() {
    7. }
    8. public Student(String name, int chinese, int math, int english) {
    9. this.name = name;
    10. this.chinese = chinese;
    11. this.math = math;
    12. this.english = english;
    13. }
    14. public String getName() {
    15. return name;
    16. }
    17. public void setName(String name) {
    18. this.name = name;
    19. }
    20. public int getChinese() {
    21. return chinese;
    22. }
    23. public void setChinese(int chinese) {
    24. this.chinese = chinese;
    25. }
    26. public int getMath() {
    27. return math;
    28. }
    29. public void setMath(int math) {
    30. this.math = math;
    31. }
    32. public int getEnglish() {
    33. return english;
    34. }
    35. public void setEnglish(int english) {
    36. this.english = english;
    37. }
    38. public int getSum() {
    39. return this.chinese + this.math + this.english;
    40. }
    41. @Override
    42. public int compareTo(Student o) {
    43. // 主要条件: 按照总分进行排序
    44. int result = o.getSum() - this.getSum();
    45. // 次要条件: 如果总分一样,就按照语文成绩排序
    46. result = result == 0 ? o.getChinese() - this.getChinese() : result;
    47. // 如果语文成绩也一样,就按照数学成绩排序
    48. result = result == 0 ? o.getMath() - this.getMath() : result;
    49. // 如果总分一样,各科成绩也都一样,就按照姓名排序
    50. result = result == 0 ? o.getName().compareTo(this.getName()) : result;
    51. return result;
    52. }
    53. }
  • 测试类

    1. public class TreeSetDemo {
    2. public static void main(String[] args) {
    3. //创建TreeSet集合对象,通过比较器排序进行排序
    4. TreeSet<Student> ts = new TreeSet<Student>();
    5. //创建学生对象
    6. Student s1 = new Student("jack", 98, 100, 95);
    7. Student s2 = new Student("rose", 95, 95, 95);
    8. Student s3 = new Student("sam", 100, 93, 98);
    9. //把学生对象添加到集合
    10. ts.add(s1);
    11. ts.add(s2);
    12. ts.add(s3);
    13. //遍历集合
    14. for (Student s : ts) {
    15. System.out.println(s.getName() + "," + s.getChinese() + "," + s.getMath() + "," + s.getEnglish() + "," + s.getSum());
    16. }
    17. }
    18. }

TreeSet是基于TreeMap底层是红黑树

10.HashSet集合

10.1 HashSet集合概述和特点【应用】

  • 底层数据结构是哈希表
  • 存取无序
  • 不可以存储重复元素
  • 没有索引,不能使用普通for循环遍历

10.2 HashSet集合的基本应用【应用】

存储字符串并遍历

  1. public class HashSetDemo {
  2. public static void main(String[] args) {
  3. //创建集合对象
  4. HashSet<String> set = new HashSet<String>();
  5. //添加元素
  6. set.add("hello");
  7. set.add("world");
  8. set.add("java");
  9. //不包含重复元素的集合
  10. set.add("world");
  11. //遍历
  12. for(String s : set) {
  13. System.out.println(s);
  14. }
  15. }
  16. }

10.3 哈希值【理解】

  • 哈希值简介
    是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
  • 如何获取哈希值
    Object类中的public int hashCode():返回对象的哈希码值
  • 哈希值的特点
    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

10.4 哈希表结构【理解】

  • JDK1.8以前
    数组 + 链表
    14_JKD8以前哈希表.png
  • 原理解析:
    1. 创建一个默认长度16,默认加载因子0.75的数组,数组名table
    2. 根据元素的哈希值跟数组的长度计算出应存入的位置
      1. 判断当前位置是否为null,如果是null直接存入
      2. 如果位置不为null,表示有元素,则调用equals方法比较属性值
        1. 如果一样,则不存,如果不一样,则存入数组,老元素挂在新元素下面

加载因子:决定什么时候扩容,数组里面存了数组长度*0.75个元素的时候就开始扩容

  • JDK1.8以后
    • 节点个数少于等于8个
      数组 + 链表
    • 节点个数多于8个,就会把链表换成红黑树
      数组 + 红黑树

15_JKD8以后哈希表.png

2020-07-15_231310.png

10.5 HashSet集合存储学生对象并遍历【应用】

  • 案例需求
    • 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
    • 要求:学生对象的成员变量值相同,我们就认为是同一个对象
  • 代码实现
    学生类

    1. public class Student {
    2. private String name;
    3. private int age;
    4. public Student() {
    5. }
    6. public Student(String name, int age) {
    7. this.name = name;
    8. this.age = age;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public int getAge() {
    17. return age;
    18. }
    19. public void setAge(int age) {
    20. this.age = age;
    21. }
    22. @Override
    23. public boolean equals(Object o) {
    24. if (this == o) return true;
    25. if (o == null || getClass() != o.getClass()) return false;
    26. Student student = (Student) o;
    27. if (age != student.age) return false;
    28. return name != null ? name.equals(student.name) : student.name == null;
    29. }
    30. @Override
    31. public int hashCode() {
    32. int result = name != null ? name.hashCode() : 0;
    33. result = 31 * result + age;
    34. return result;
    35. }
    36. }
  • 测试类

    1. public class HashSetDemo02 {
    2. public static void main(String[] args) {
    3. //创建HashSet集合对象
    4. HashSet<Student> hs = new HashSet<Student>();
    5. //创建学生对象
    6. Student s1 = new Student("林青霞", 30);
    7. Student s2 = new Student("张曼玉", 35);
    8. Student s3 = new Student("王祖贤", 33);
    9. Student s4 = new Student("王祖贤", 33);
    10. //把学生添加到集合
    11. hs.add(s1);
    12. hs.add(s2);
    13. hs.add(s3);
    14. hs.add(s4);
    15. //遍历集合(增强for)
    16. for (Student s : hs) {
    17. System.out.println(s.getName() + "," + s.getAge());
    18. }
    19. }
    20. }

10.6 小结

2020-07-15_155427.png
既要唯一还有存取有序

  1. LinkedHashSet

11.Map集合

01_集合类体系结构图.png

11.1 Map集合概述和特点【理解】

  • Map集合概述

    1. interface Map<K,V> K:键的类型;V:值的类型
  • Map集合的特点

    • 双列集合,一个键对应一个值
    • 键不可以重复值可以重复
    • (键+值)这个整体,我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象“
  • Map集合的基本使用

    1. public class MapDemo01 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Map<String,String> map = new HashMap<String,String>();
    5. //将指定的值与该映射中的指定键相关联
    6. map.put("itheima001","林青霞");
    7. map.put("itheima002","张曼玉");
    8. map.put("itheima003","王祖贤");
    9. map.put("itheima003","柳岩");
    10. //输出集合对象
    11. System.out.println(map);
    12. }
    13. }

11.2 Map集合的基本功能【应用】

  • 方法介绍 | 方法名 | 说明 | | —- | —- | | V put(K key,V value) | 添加元素 | | V remove(Object key) | 根据键删除键值对元素 | | void clear() | 移除所有的键值对元素 | | boolean containsKey(Object key) | 判断集合是否包含指定的键 | | boolean containsValue(Object value) | 判断集合是否包含指定的值 | | boolean isEmpty() | 判断集合是否为空 | | int size() | 集合的长度,也就是集合中键值对的个数 |

  • 示例代码

    1. /**
    2. * Map常用方法
    3. */
    4. public class MainClass2 {
    5. public static void main(String[] args) {
    6. Map<String,String> map = new HashMap<>();
    7. map.put("itheima001","小智");
    8. map.put("itheima002","小美");
    9. map.put("itheima003","大胖");
    10. map.put("itheima004","小黑");
    11. map.put("itheima005","大师");
    12. //method1(map);
    13. //method2(map);
    14. //method3(map);
    15. //method4(map);
    16. //method5(map);
    17. //method6(map);
    18. //method7(map);
    19. }
    20. private static void method7(Map<String, String> map) {
    21. // int size() 集合的长度,也就是集合中键值对的个数
    22. int size = map.size();
    23. System.out.println(size);
    24. }
    25. private static void method6(Map<String, String> map) {
    26. // boolean isEmpty() 判断集合是否为空
    27. boolean empty1 = map.isEmpty();
    28. System.out.println(empty1);//false
    29. map.clear();
    30. boolean empty2 = map.isEmpty();
    31. System.out.println(empty2);//true
    32. }
    33. private static void method5(Map<String, String> map) {
    34. // boolean containsValue(Object value) 判断集合是否包含指定的值
    35. boolean result1 = map.containsValue("aaa");
    36. boolean result2 = map.containsValue("小智");
    37. System.out.println(result1);
    38. System.out.println(result2);
    39. }
    40. private static void method4(Map<String, String> map) {
    41. // boolean containsKey(Object key) 判断集合是否包含指定的键
    42. boolean result1 = map.containsKey("itheima001");
    43. boolean result2 = map.containsKey("itheima006");
    44. System.out.println(result1);
    45. System.out.println(result2);
    46. }
    47. private static void method3(Map<String, String> map) {
    48. // void clear() 移除所有的键值对元素
    49. map.clear();
    50. System.out.println(map);
    51. }
    52. private static void method2(Map<String, String> map) {
    53. // V remove(Object key) 根据键删除键值对元素
    54. String s = map.remove("itheima001");
    55. System.out.println(s);
    56. System.out.println(map);
    57. }
    58. private static void method1(Map<String, String> map) {
    59. // V put(K key,V value) 添加元素
    60. //如果要添加的键不存在,那么会把键值对都添加到集合中
    61. //如果要添加的键是存在的,那么会覆盖原先的值,把原先值当做返回值进行返回。
    62. String s = map.put("itheima001", "aaa");
    63. System.out.println(s);
    64. System.out.println(map);
    65. }
    66. }

11.3 Map集合的获取功能【应用】

  • 方法介绍 | 方法名 | 说明 | | —- | —- | | V get(Object key) | 根据键获取值 | | Set keySet() | 获取所有键的集合 | | Collection values() | 获取所有值的集合 | | Set> entrySet() | 获取所有键值对对象的集合 |

11.4 Map集合的遍历(方式1)【应用】

  • 遍历思路
    • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
      • 把所有的丈夫给集中起来
      • 遍历丈夫的集合,获取到每一个丈夫
      • 根据丈夫去找对应的妻子
  • 步骤分析
    • 获取所有键的集合。用keySet()方法实现
    • 遍历键的集合,获取到每一个键。用增强for实现
    • 根据键去找值。用get(Object key)方法实现
  • 代码实现

    1. /**
    2. * Map的遍历方式-1
    3. */
    4. public class MainClass3 {
    5. public static void main(String[] args) {
    6. //创建集合并添加元素
    7. Map<String,String> map = new HashMap<>();
    8. map.put("1号丈夫","1号妻子");
    9. map.put("2号丈夫","2号妻子");
    10. map.put("3号丈夫","3号妻子");
    11. map.put("4号丈夫","4号妻子");
    12. map.put("5号丈夫","5号妻子");
    13. //获取到所有的键
    14. Set<String> keys = map.keySet();
    15. //遍历Set集合得到每一个键
    16. for (String key : keys) {
    17. //通过每一个键key,来获取到对应的值
    18. String value = map.get(key);
    19. System.out.println(key + "---" + value);
    20. }
    21. }
    22. }

11.5 Map集合的遍历(方式2)【应用】

  • 遍历思路
    • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
      • 获取所有结婚证的集合
      • 遍历结婚证的集合,得到每一个结婚证
      • 根据结婚证获取丈夫和妻子
  • 步骤分析
    • 获取所有键值对对象的集合
      • Set> entrySet():获取所有键值对对象的集合
    • 遍历键值对对象的集合,得到每一个键值对对象
      • 用增强for实现,得到每一个Map.Entry
    • 根据键值对对象获取键和值
      • 用getKey()得到键
      • 用getValue()得到值
  • 代码实现

    1. /**
    2. * Map的遍历方式-2
    3. */
    4. public class MainClass4 {
    5. public static void main(String[] args) {
    6. //创建集合并添加元素
    7. Map<String,String> map = new HashMap<>();
    8. map.put("1号丈夫","1号妻子");
    9. map.put("2号丈夫","2号妻子");
    10. map.put("3号丈夫","3号妻子");
    11. map.put("4号丈夫","4号妻子");
    12. map.put("5号丈夫","5号妻子");
    13. //首先要获取到所有的键值对对象。
    14. //Set集合中装的是键值对对象(Entry对象)
    15. //而Entry里面装的是键和值
    16. Set<Map.Entry<String, String>> entries = map.entrySet();
    17. for (Map.Entry<String, String> entry : entries) {
    18. //得到每一个键值对对象
    19. String key = entry.getKey();
    20. String value = entry.getValue();
    21. System.out.println(key + "---" + value);
    22. }
    23. }
    24. }

11.6 HashMap-原理解析

  • HashMap底层原理
    • HashMap底层是哈希表结构的
    • 依赖hashCode方法和equals方法保证键的唯一
    • 如果键要存储的是自定义对象,需要重写hashCode和equals方法

12.HashMap集合

12.1 HashMap集合概述和特点【理解】

  • HashMap底层是哈希表结构的
  • 键值部分依赖hashCode方法和equals方法保证键的唯一
  • 如果要存储的是自定义对象,需要重写hashCode和equals方法

12.2 HashMap集合应用案例【应用】

  • 案例需求
    • 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
    • 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
  • 代码实现
    学生类

    1. public class Student {
    2. private String name;
    3. private int age;
    4. public Student() {
    5. }
    6. public Student(String name, int age) {
    7. this.name = name;
    8. this.age = age;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public int getAge() {
    17. return age;
    18. }
    19. public void setAge(int age) {
    20. this.age = age;
    21. }
    22. @Override
    23. public boolean equals(Object o) {
    24. if (this == o) return true;
    25. if (o == null || getClass() != o.getClass()) return false;
    26. Student student = (Student) o;
    27. if (age != student.age) return false;
    28. return name != null ? name.equals(student.name) : student.name == null;
    29. }
    30. @Override
    31. public int hashCode() {
    32. int result = name != null ? name.hashCode() : 0;
    33. result = 31 * result + age;
    34. return result;
    35. }
    36. }
  • 测试类

    1. /**
    2. * Map的练习
    3. */
    4. public class MyMap5 {
    5. public static void main(String[] args) {
    6. HashMap<Student,String> hm = new HashMap<>();
    7. Student s1 = new Student("xiaohei",23);
    8. Student s2 = new Student("dapang",22);
    9. Student s3 = new Student("xiaomei",22);
    10. hm.put(s1,"江苏");
    11. hm.put(s2,"北京");
    12. hm.put(s3,"天津");
    13. //第一种:先获取到所有的键,再通过每一个键来找对应的值
    14. Set<Student> keys = hm.keySet();
    15. for (Student key : keys) {
    16. String value = hm.get(key);
    17. System.out.println(key + "----" + value);
    18. }
    19. System.out.println("===================================");
    20. //第二种:先获取到所有的键值对对象。再获取到里面的每一个键和每一个值
    21. Set<Map.Entry<Student, String>> entries = hm.entrySet();
    22. for (Map.Entry<Student, String> entry : entries) {
    23. Student key = entry.getKey();
    24. String value = entry.getValue();
    25. System.out.println(key + "----" + value);
    26. }
    27. System.out.println("===================================");
    28. //第三种:
    29. hm.forEach(
    30. (Student key, String value)->{
    31. System.out.println(key + "----" + value);
    32. }
    33. );
    34. }
    35. }

13.TreeMap集合

13.1 TreeMap集合概述和特点【理解】

  • TreeMap底层是红黑树结构
  • 依赖自然排序或者比较器排序,对键进行排序
  • 如果存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则

13.2 TreeMap集合应用案例【应用】

  • 案例需求
    • 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
    • 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
  • 代码实现
    学生类

    1. public class Student implements Comparable<Student>{
    2. private String name;
    3. private int age;
    4. public Student() {
    5. }
    6. public Student(String name, int age) {
    7. this.name = name;
    8. this.age = age;
    9. }
    10. public String getName() {
    11. return name;
    12. }
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public int getAge() {
    17. return age;
    18. }
    19. public void setAge(int age) {
    20. this.age = age;
    21. }
    22. @Override
    23. public String toString() {
    24. return "Student{" +
    25. "name='" + name + '\'' +
    26. ", age=" + age +
    27. '}';
    28. }
    29. @Override
    30. public int compareTo(Student o) {
    31. //按照年龄进行排序
    32. int result = o.getAge() - this.getAge();
    33. //次要条件,按照姓名排序。
    34. result = result == 0 ? o.getName().compareTo(this.getName()) : result;
    35. return result;
    36. }
    37. }
  • 测试类

    1. public class Test1 {
    2. public static void main(String[] args) {
    3. // 创建TreeMap集合对象
    4. TreeMap<Student,String> tm = new TreeMap<>();
    5. // 创建学生对象
    6. Student s1 = new Student("xiaohei",23);
    7. Student s2 = new Student("dapang",22);
    8. Student s3 = new Student("xiaomei",22);
    9. // 将学生对象添加到TreeMap集合中
    10. tm.put(s1,"江苏");
    11. tm.put(s2,"北京");
    12. tm.put(s3,"天津");
    13. // 遍历TreeMap集合,打印每个学生的信息
    14. tm.forEach(
    15. (Student key, String value)->{
    16. System.out.println(key + "---" + value);
    17. }
    18. );
    19. }
    20. }