## 1.Collection集合 ### 1.1数组和集合的区别【理解】 - 相同点 都是容器,可以存储多个数据 - 不同点 - 数组的长度是不可变的,集合的长度是可变的 - 数组可以存基本数据类型和引用数据类型 集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类 ### 1.2集合类体系结构【理解】 ![01_集合类体系结构图.png](../../../_resources/01_集合类体系结构图.png) ### 1.3Collection 集合概述和使用【应用】 - 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.4Collection集合的遍历 #### 1.4.1 迭代器遍历 - 迭代器介绍 - 迭代器,集合的专用遍历方式 - Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到 - Iterator中的常用方法 ​ boolean hasNext(): 判断当前位置是否有元素可以被取出 ​ E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置 - Collection集合的遍历 ```java public class IteratorDemo1 { public static void main(String[] args) { //创建集合对象 Collection c = new ArrayList<>(); //添加元素 c.add("hello"); c.add("world"); c.add("java"); c.add("javaee"); //Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到 Iterator it = c.iterator(); //用while循环改进元素的判断和获取 while (it.hasNext()) { String s = it.next(); System.out.println(s); } } } ``` - 迭代器中删除的方法 ​ void remove(): 删除迭代器对象当前指向的元素 ```java public class IteratorDemo2 { public static void main(String[] args) { ArrayList list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("b"); list.add("c"); list.add("d"); Iterator it = list.iterator(); while(it.hasNext()){ String s = it.next(); if("b".equals(s)){ //指向谁,那么此时就删除谁. it.remove(); } } System.out.println(list); } } ``` #### 1.4.2 增强for - 介绍 - 它是JDK5之后出现的,其内部原理是一个Iterator迭代器 - 实现Iterable接口的类才可以使用迭代器和增强for - 简化数组和Collection集合的遍历 - 格式 ​ for(集合/数组中元素的数据类型 变量名 : 集合/数组名) { ​ // 已经将当前遍历到的元素封装到变量中了,直接使用变量即可 ​ } - 代码 ```java public class MyCollectonDemo1 { public static void main(String[] args) { ArrayList list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); //1,数据类型一定是集合或者数组中元素的类型 //2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素 //3,list就是要遍历的集合或者数组 for(String str : list){ System.out.println(str); } } } ``` - 细节点注意: 1.报错NoSuchElementException 2.迭代器遍历完毕,指针不会复位 3.循环中只能用一次next方法 4.迭代器遍历时,不能用集合的方法进行增加或者删除 ```java public class A04_CollectionDemo4 { public static void main(String[] args) { /* 迭代器的细节注意点: 1.报错NoSuchElementException 2.迭代器遍历完毕,指针不会复位 3.循环中只能用一次next方法 4.迭代器遍历时,不能用集合的方法进行增加或者删除 暂时当做一个结论先行记忆,在今天我们会讲解源码详细的再来分析。 如果我实在要删除:那么可以用迭代器提供的remove方法进行删除。 如果我要添加,暂时没有办法。(只是暂时) */ //1.创建集合并添加元素 Collection coll = new ArrayList<>(); coll.add("aaa"); coll.add("bbb"); coll.add("ccc"); coll.add("ddd"); //2.获取迭代器对象 //迭代器就好比是一个箭头,默认指向集合的0索引处 Iterator it = coll.iterator(); //3.利用循环不断的去获取集合中的每一个元素 while(it.hasNext()){ //4.next方法的两件事情:获取元素并移动指针 String str = it.next(); System.out.println(str); } //当上面循环结束之后,迭代器的指针已经指向了最后没有元素的位置 //System.out.println(it.next());//NoSuchElementException //迭代器遍历完毕,指针不会复位 System.out.println(it.hasNext()); //如果我们要继续第二次遍历集合,只能再次获取一个新的迭代器对象 Iterator it2 = coll.iterator(); while(it2.hasNext()){ String str = it2.next(); System.out.println(str); } } } ``` #### 1.4.3 lambda表达式 ​ 利用forEach方法,再结合lambda表达式的方式进行遍历 ```java public class A07_CollectionDemo7 { public static void main(String[] args) { /* lambda表达式遍历: default void forEach(Consumer action): */ //1.创建集合并添加元素 Collection coll = new ArrayList<>(); coll.add("zhangsan"); coll.add("lisi"); coll.add("wangwu"); //2.利用匿名内部类的形式 //底层原理: //其实也会自己遍历集合,依次得到每一个元素 //把得到的每一个元素,传递给下面的accept方法 //s依次表示集合中的每一个数据 /* coll.forEach(new Consumer() { @Override public void accept(String s) { System.out.println(s); } });*/ //lambda表达式 coll.forEach(s -> System.out.println(s)); } } ``` ## 2.List集合 ### 2.1List集合的概述和特点【记忆】 - List集合的概述 - 有序集合,这里的有序指的是存取顺序 - 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素 - 与Set集合不同,列表通常允许重复的元素 - List集合的特点 - 存取有序 - 可以重复 - 有索引 ### 2.2List集合的特有方法【应用】 - 方法介绍 | 方法名 | 描述 | | --- | --- | | void add(int index,E element) | 在此集合中的指定位置插入指定的元素 | | E remove(int index) | 删除指定索引处的元素,返回被删除的元素 | | E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 | | E get(int index) | 返回指定索引处的元素 | - 示例代码 ```java public class MyListDemo { public static void main(String[] args) { List list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); //method1(list); //method2(list); //method3(list); //method4(list); } private static void method4(List list) { // E get(int index) 返回指定索引处的元素 String s = list.get(0); System.out.println(s); } private static void method3(List list) { // E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 //被替换的那个元素,在集合中就不存在了. String result = list.set(0, "qqq"); System.out.println(result); System.out.println(list); } private static void method2(List list) { // E remove(int index) 删除指定索引处的元素,返回被删除的元素 //在List集合中有两个删除的方法 //第一个 删除指定的元素,返回值表示当前元素是否删除成功 //第二个 删除指定索引的元素,返回值表示实际删除的元素 String s = list.remove(0); System.out.println(s); System.out.println(list); } private static void method1(List list) { // void add(int index,E element) 在此集合中的指定位置插入指定的元素 //原来位置上的元素往后挪一个索引. list.add(0,"qqq"); System.out.println(list); } } ``` ### 2.3List集合的五种遍历方式【应用】 1. 迭代器 2. 列表迭代器 3. 增强for 4. Lambda表达式 5. 普通for循环 代码示例: ```java //创建集合并添加元素 List list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); //1.迭代器 /*Iterator it = list.iterator(); while(it.hasNext()){ String str = it.next(); System.out.println(str); }*/ //2.增强for //下面的变量s,其实就是一个第三方的变量而已。 //在循环的过程中,依次表示集合中的每一个元素 /* for (String s : list) { System.out.println(s); }*/ //3.Lambda表达式 //forEach方法的底层其实就是一个循环遍历,依次得到集合中的每一个元素 //并把每一个元素传递给下面的accept方法 //accept方法的形参s,依次表示集合中的每一个元素 //list.forEach(s->System.out.println(s) ); //4.普通for循环 //size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素 /*for (int i = 0; i < list.size(); i++) { //i:依次表示集合中的每一个索引 String s = list.get(i); System.out.println(s); }*/ // 5.列表迭代器 //获取一个列表迭代器的对象,里面的指针默认也是指向0索引的 //额外添加了一个方法:在遍历的过程中,可以添加元素 ListIterator it = list.listIterator(); while(it.hasNext()){ String str = it.next(); if("bbb".equals(str)){ //qqq it.add("qqq"); } } System.out.println(list); ``` ### 2.4 细节点注意: List系列集合中的两个删除的方法 ```java 1.直接删除元素 2.通过索引进行删除 ``` 代码示例: ```java //List系列集合中的两个删除的方法 //1.直接删除元素 //2.通过索引进行删除 //1.创建集合并添加元素 List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); //2.删除元素 //请问:此时删除的是1这个元素,还是1索引上的元素? //为什么? //因为在调用方法的时候,如果方法出现了重载现象 //优先调用,实参跟形参类型一致的那个方法。 //list.remove(1); //手动装箱,手动把基本数据类型的1,变成Integer类型 Integer i = Integer.valueOf(1); list.remove(i); System.out.println(list); ``` ## 3.数据结构 ### 3.1数据结构之栈和队列【记忆】 - 栈结构 ​ 先进后出 - 队列结构 ​ 先进先出 ### 3.2数据结构之数组和链表【记忆】 - 数组结构 ​ 查询快、增删慢 - 队列结构 ​ 查询慢、增删快 ## 4.List集合的实现类 ### 4.1List集合子类的特点【记忆】 - ArrayList集合 ​ 底层是数组结构实现,查询快、增删慢 - LinkedList集合 ​ 底层是链表结构实现,查询慢、增删快 ### 4.2LinkedList集合的特有功能【应用】 - 特有方法 | 方法名 | 说明 | | --- | --- | | public void addFirst(E e) | 在该列表开头插入指定的元素 | | public void addLast(E e) | 将指定的元素追加到此列表的末尾 | | public E getFirst() | 返回此列表中的第一个元素 | | public E getLast() | 返回此列表中的最后一个元素 | | public E removeFirst() | 从此列表中删除并返回第一个元素 | | public E removeLast() | 从此列表中删除并返回最后一个元素 | - 示例代码 ```java public class MyLinkedListDemo4 { public static void main(String[] args) { LinkedList list = new LinkedList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); // public void addFirst(E e) 在该列表开头插入指定的元素 //method1(list); // public void addLast(E e) 将指定的元素追加到此列表的末尾 //method2(list); // public E getFirst() 返回此列表中的第一个元素 // public E getLast() 返回此列表中的最后一个元素 //method3(list); // public E removeFirst() 从此列表中删除并返回第一个元素 // public E removeLast() 从此列表中删除并返回最后一个元素 //method4(list); } private static void method4(LinkedList list) { String first = list.removeFirst(); System.out.println(first); String last = list.removeLast(); System.out.println(last); System.out.println(list); } private static void method3(LinkedList list) { String first = list.getFirst(); String last = list.getLast(); System.out.println(first); System.out.println(last); } private static void method2(LinkedList list) { list.addLast("www"); System.out.println(list); } private static void method1(LinkedList list) { list.addFirst("qqq"); System.out.println(list); } } ``` ## 5\. 源码分析 ### 5.1 ArrayList源码分析: 核心步骤: 1. 创建ArrayList对象的时候,他在底层先创建了一个长度为0的数组。 数组名字:elementDate,定义变量size。 size这个变量有两层含义: ①:元素的个数,也就是集合的长度 ②:下一个元素的存入位置 2. 添加元素,添加完毕后,size++ 扩容时机一: 3. 当存满时候,会创建一个新的数组,新数组的长度,是原来的1.5倍,也就是长度为15.再把所有的元素,全拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那么下次还会继续扩容,还是1.5倍。 扩容时机二: 4. 一次性添加多个数据,扩容1.5倍不够,怎么办呀? 如果一次添加多个元素,1.5倍放不下,那么新创建数组的长度以实际为准。 举个例子: 在一开始,如果默认的长度为10的数组已经装满了,在装满的情况下,我一次性要添加100个数据很显然,10扩容1.5倍,变成15,还是不够, 怎么办? 此时新数组的长度,就以实际情况为准,就是110 具体分析过程可以参见视频讲解。 **添加一个元素时的扩容:** ![第一次添加数据.png](../../../_resources/第一次添加数据.png) **添加多个元素时的扩容:** ![第11次添加数据.png](../../../_resources/第11次添加数据.png) ### 5.2 LinkedList源码分析: 底层是双向链表结构 核心步骤如下: 1. 刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认为null 2. 添加第一个元素时,底层创建一个结点对象,first和last都记录这个结点的地址值 3. 添加第二个元素时,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值 具体分析过程可以参见视频讲解。 ![LinkedList源码分析.png](../../../_resources/LinkedList源码分析.png) ### 5.3 迭代器源码分析: 迭代器遍历相关的三个方法: - Iterator iterator() :获取一个迭代器对象 - boolean hasNext() :判断当前指向的位置是否有元素 - E next() :获取当前指向的元素并移动指针 ![迭代器源码分析.png](../../../_resources/迭代器源码分析.png) ## 1.泛型 ### 1.1泛型概述 - 泛型的介绍 ​ 泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制 - 泛型的好处 1. 把运行时期的问题提前到了编译期间 2. 避免了强制类型转换 - 泛型的定义格式 - <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如: - <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V> ![D45A95B055A9DFFF3F88B3362E6AC934.jpg](../../../_resources/D45A95B055A9DFFF3F88B3362E6AC934.jpg) ## 2.数据结构 ### 2.1二叉树【理解】 - 二叉树的特点 - 二叉树中,任意一个节点的度要小于等于2 - 节点: 在树结构中,每一个元素称之为节点 - 度: 每一个节点的子节点数量称之为度 - 二叉树结构图 ![01_二叉树结构图.png](../../../_resources/01_二叉树结构图.png) ### 2.2二叉查找树【理解】 - 二叉查找树的特点 - 二叉查找树,又称二叉排序树或者二叉搜索树 - 每一个节点上最多有两个子节点 - 左子树上所有节点的值都小于根节点的值 - 右子树上所有节点的值都大于根节点的值 - 二叉查找树结构图 ![02_二叉查找树结构图.png](../../../_resources/02_二叉查找树结构图.png) - 二叉查找树和二叉树对比结构图 ![03_二叉查找树和二叉树对比结构图.png](../../../_resources/03_二叉查找树和二叉树对比结构图.png) - 二叉查找树添加节点规则 - 小的存左边 - 大的存右边 - 一样的不存 ![04_二叉查找树添加节点规则.png](../../../_resources/04_二叉查找树添加节点规则.png) ### 2.3平衡二叉树【理解】 - 平衡二叉树的特点 - 二叉树左右两个子树的高度差不超过1 - 任意节点的左右两个子树都是一颗平衡二叉树 - 平衡二叉树旋转 - 旋转触发时机 - 当添加一个节点之后,该树不再是一颗平衡二叉树 - 左旋 - 就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点 ![05_平衡二叉树左旋01.png](../../../_resources/05_平衡二叉树左旋01.png) ![05_平衡二叉树左旋02.png](../../../_resources/05_平衡二叉树左旋02.png) - 右旋 - 就是将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节点当左子节点 ![06_平衡二叉树右旋01.png](../../../_resources/06_平衡二叉树右旋01.png) ![06_平衡二叉树右旋02.png](../../../_resources/06_平衡二叉树右旋02.png) - 平衡二叉树和二叉查找树对比结构图 ![07_平衡二叉树和二叉查找树对比结构图.png](../../../_resources/07_平衡二叉树和二叉查找树对比结构图.png) - 平衡二叉树旋转的四种情况 - 左左 - 左左: 当根节点左子树的左子树有节点插入,导致二叉树不平衡 - 如何旋转: 直接对整体进行右旋即可 ![08_平衡二叉树左左.png](../../../_resources/08_平衡二叉树左左.png) - 左右 - 左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡 - 如何旋转: 先在左子树对应的节点位置进行左旋,在对整体进行右旋 ![09_平衡二叉树左右.png](../../../_resources/09_平衡二叉树左右.png) - 右右 - 右右: 当根节点右子树的右子树有节点插入,导致二叉树不平衡 - 如何旋转: 直接对整体进行左旋即可 ![10_平衡二叉树右右.png](../../../_resources/10_平衡二叉树右右.png) - 右左 - 右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡 - 如何旋转: 先在右子树对应的节点位置进行右旋,在对整体进行左旋 ![11_平衡二叉树右左.png](../../../_resources/11_平衡二叉树右左.png) ### 2.3红黑树【理解】 - 红黑树的特点 - 平衡二叉B树 - 每一个节点可以是红或者黑 - 红黑树不是高度平衡的,它的平衡是通过"自己的红黑规则"进行实现的 - 红黑树的红黑规则有哪些 1. 每一个节点或是红色的,或者是黑色的 2. 根节点必须是黑色 3. 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的 4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况) 5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点 ![12_红黑树结构图.png](../../../_resources/12_红黑树结构图.png) - 红黑树添加节点的默认颜色 - 添加节点时,默认为红色,效率高 ![13_红黑树添加节点颜色.png](../../../_resources/13_红黑树添加节点颜色.png) - 红黑树添加节点后如何保持红黑规则 ![F5B26684054D864E36F0FB4846C11EC8.jpg](../../../_resources/F5B26684054D864E36F0FB4846C11EC8.jpg) ## 3.Set集合 ### 3.1Set集合概述和特点【应用】 - 不可以存储重复元素 - 没有索引,不能使用普通for循环遍历 ### 3.2Set集合的使用【应用】 存储字符串并遍历 ```java public class MySet1 { public static void main(String[] args) { //创建集合对象 Set set = new TreeSet<>(); //添加元素 set.add("ccc"); set.add("aaa"); set.add("aaa"); set.add("bbb"); // for (int i = 0; i < set.size(); i++) { // //Set集合是没有索引的,所以不能使用通过索引获取元素的方法 // } //遍历集合 Iterator it = set.iterator(); while (it.hasNext()){ String s = it.next(); System.out.println(s); } System.out.println("-----------------------------------"); for (String s : set) { System.out.println(s); } } } ``` ## 4.TreeSet集合 ### 4.1TreeSet集合概述和特点【应用】 - 不可以存储重复元素 - 没有索引 - 可以将元素按照规则进行排序 - TreeSet():根据其元素的自然排序进行排序 - TreeSet(Comparator comparator) :根据指定的比较器进行排序 ### 4.2TreeSet集合基本使用【应用】 存储Integer类型的整数并遍历 ```java public class TreeSetDemo01 { public static void main(String[] args) { //创建集合对象 TreeSet ts = new TreeSet(); //添加元素 ts.add(10); ts.add(40); ts.add(30); ts.add(50); ts.add(20); ts.add(30); //遍历集合 for(Integer i : ts) { System.out.println(i); } } } ``` ### 4.3自然排序Comparable的使用【应用】 - 案例需求 - 存储学生对象并遍历,创建TreeSet集合使用无参构造方法 - 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序 - 实现步骤 1. 使用空参构造创建TreeSet集合 - 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的 2. 自定义的Student类实现Comparable接口 - 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法 3. 重写接口中的compareTo方法 - 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写 - 代码实现 学生类 ```java public class Student implements Comparable{ 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 = this.age - o.age; //次要判断条件: 年龄相同时,按照姓名的字母顺序排序 result = result == 0 ? this.name.compareTo(o.getName()) : result; return result; } } ``` 测试类 ```java public class MyTreeSet2 { public static void main(String[] args) { //创建集合对象 TreeSet ts = new TreeSet<>(); //创建学生对象 Student s1 = new Student("zhangsan",28); Student s2 = new Student("lisi",27); Student s3 = new Student("wangwu",29); Student s4 = new Student("zhaoliu",28); Student s5 = new Student("qianqi",30); //把学生添加到集合 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); //遍历集合 for (Student student : ts) { System.out.println(student); } } } ``` ### 4.4比较器排序Comparator的使用【应用】 - 案例需求 - 存储老师对象并遍历,创建TreeSet集合使用带参构造方法 - 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序 - 实现步骤 - 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的 - 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法 - 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写 - 代码实现 老师类 ```java public class Teacher { private String name; private int age; public Teacher() { } public Teacher(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 "Teacher{" + "name='" + name + '\'' + ", age=" + age + '}'; } } ``` 测试类 ```java public class MyTreeSet4 { public static void main(String[] args) { //创建集合对象 TreeSet ts = new TreeSet<>(new Comparator() { @Override public int compare(Teacher o1, Teacher o2) { //o1表示现在要存入的那个元素 //o2表示已经存入到集合中的元素 //主要条件 int result = o1.getAge() - o2.getAge(); //次要条件 result = result == 0 ? o1.getName().compareTo(o2.getName()) : result; return result; } }); //创建老师对象 Teacher t1 = new Teacher("zhangsan",23); Teacher t2 = new Teacher("lisi",22); Teacher t3 = new Teacher("wangwu",24); Teacher t4 = new Teacher("zhaoliu",24); //把老师添加到集合 ts.add(t1); ts.add(t2); ts.add(t3); ts.add(t4); //遍历集合 for (Teacher teacher : ts) { System.out.println(teacher); } } } ``` ### 4.5两种比较方式总结【理解】 - 两种比较方式小结 - 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序 - 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序 - 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序 - 两种方式中关于返回值的规则 - 如果返回值为负数,表示当前存入的元素是较小值,存左边 - 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存 - 如果返回值为正数,表示当前存入的元素是较大值,存右边 ## 5.HashSet集合 ### 5.1HashSet集合概述和特点【应用】 - 底层数据结构是哈希表 - 存取无序 - 不可以存储重复元素 - 没有索引,不能使用普通for循环遍历 ### 5.2HashSet集合的基本应用【应用】 存储字符串并遍历 ```java public class HashSetDemo { public static void main(String[] args) { //创建集合对象 HashSet set = new HashSet(); //添加元素 set.add("hello"); set.add("world"); set.add("java"); //不包含重复元素的集合 set.add("world"); //遍历 for(String s : set) { System.out.println(s); } } } ``` ### 5.3哈希值【理解】 - 哈希值简介 ​ 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值 - 如何获取哈希值 ​ Object类中的public int hashCode():返回对象的哈希码值 - 哈希值的特点 - 同一个对象多次调用hashCode()方法返回的哈希值是相同的 - 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同 ### 5.4哈希表结构【理解】 - JDK1.8以前 ​ 数组 + 链表 ![14_JKD8以前哈希表.png](../../../_resources/14_JKD8以前哈希表.png) - JDK1.8以后 - 节点个数少于等于8个 ​ 数组 + 链表 - 节点个数多于8个 ​ 数组 + 红黑树 ![15_JKD8以后哈希表.png](../../../_resources/15_JKD8以后哈希表.png) ### 5.5HashSet集合存储学生对象并遍历【应用】 - 案例需求 - 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合 - 要求:学生对象的成员变量值相同,我们就认为是同一个对象 - 代码实现 学生类 ```java 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; } } ``` 测试类 ```java public class HashSetDemo02 { public static void main(String[] args) { //创建HashSet集合对象 HashSet hs = new HashSet(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); Student s4 = new Student("王祖贤", 33); //把学生添加到集合 hs.add(s1); hs.add(s2); hs.add(s3); hs.add(s4); //遍历集合(增强for) for (Student s : hs) { System.out.println(s.getName() + "," + s.getAge()); } } } ``` - 总结 ​ HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法