今日内容
- Set
- HashSet
- TreeSet
- 相关数据结构
- Map
==1. Set==
1.1 概述
Set
集合是单列集合的另一个分支。单列集合中所有的共享特性,set
都具有:
-
作为容器可以存储元素,只能存储引用数据类型数据
-
可以使用迭代器遍历
1.2 Set特点
-
唯一。元素不可重复,可以使用
set
实现去重。 -
无序。无法保证取出的顺序和存入的顺序一致。
-
没有索引。不能通过索引操作元素(通过索引获取、删除、修改等)
Set
是一个接口,HashSet
是一个他的实现类。
-
演示代码
package com.itheia; import java.util.HashSet; /** * Set通用特点演示 */ public class SetDemo01 { public static void main(String[] args) { // 创建set集合对象 HashSet<String> set = new HashSet<>(); // 添加元素 set.add("bbb"); set.add("aaa"); set.add("ddd"); set.add("ccc"); // 验证是否无序:[aaa, ccc, bbb, ddd] System.out.println("set = " + set); // 验证是否不可重复(唯一) System.out.println("set.add(\"aaa\") = " + set.add("aaa")); System.out.println("set = " + set); // 验证无索引 // set.get(1); } }
==1.3 HashSet==
1.3.1 概述
HashSet是单列集合Set常见的实现类之一。
底层数据结构是Hash表;但是JDK版本的不同,Hash表的实现原理略有差异:
-
JDK8以前:哈希表 = 数组+链表
-
JDK8及以后:哈希表 = 数组+链表+红黑树
其中的Hash值又称为散列值,是对象hashCode方法的返回值。hashCode方法定义在Object中,但是其值生成可能和内存有关,我们一般会重写hashCode,让其返回值和成员变量相关,且
-
同一个对象,多次调用hashCode方法返回值应该一样
-
不同的对象,其hashCode返回值应该尽量不一样
但是又无法保证不同对象的hash值必然不同,如果出现相同的情况,称为hash冲突。
Hash表性能比较均衡,增删改查都比较快;但是不适合范围查询。
1.3.2 数据结构
我们发现往HashSet集合中存储元素时,底层调用了元素的两个方法:一个是hashCode方法获取元素的hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。
-
只有新添加元素的hashCode值和集合中以后元素的hashCode值相同、新添加的元素调用equals方法和集合中已有元素比较结果为true, 才认为元素重复。
-
如果hashCode值相同,equals比较不同,则以链表的形式连接在数组的同一个索引为位置(如上图所示)
在JDK8开始后,为了提高性能,当链表的长度超过8时,就会把链表转换为红黑树,如下图所示:
相关概念:
-
Hash值:散列值,hashCode方法返回值;
-
同一个对象,多次调用hashCode方法的返回值应该一样
-
不同的对象的hash值尽量应该不一样。
-
自定义类一般会重写hashCode方法,让该方法的返回值与成员变量相关,且尽量与其他对象的hashCode返回值不同。
1.3.3 HashSet去重原理
原理分析:
-
一个元素想要存入Hash表中,需要依赖两个方法:hashCode、equals
-
HashSet集合,想要实现元素不重复,依赖的也是这两个方法
-
使用HashSet存储自定义对象Student(name,age,height)
演示代码:
-
实体类Student.java
package com.itheia; /** * HashSet去重原理演示:自定义实体类 */ public class Student { private String name; private int age; private double height; public Student() { } public Student(String name, int age, double height) { this.name = name; this.age = age; this.height = height; } // getter & setter 自己编写 @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}'; } /** * 比较的是属性值。如果属性值都一样,则认为是同一个对象,返回true * @param o * @return */ @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; if (Double.compare(student.height, height) != 0) return false; return name != null ? name.equals(student.name) : student.name == null; } /** * 1. hash值和成员变量的值相关,只要变量值不变,对象的hash值有不会变化 * 2. 通过相对复杂的计算逻辑,尽量实现不同对象的hash值不同 * @return */ @Override public int hashCode() { int result; long temp; result = name != null ? name.hashCode() : 0; result = 31 * result + age; temp = Double.doubleToLongBits(height); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } }
-
测试类SetDemo02.java
package com.itheia; import java.util.HashSet; /** * HashSet去重原理演示:测试类 */ public class SetDemo02 { public static void main(String[] args) { // 创建set集合对象 HashSet<Student> stus = new HashSet<>(); // 创建元素对象并添加进集合 stus.add(new Student("Ikun",18,188.0)); stus.add(new Student("华强",19,189.0)); stus.add(new Student("芳芳",88,158.0)); stus.add(new Student("Ikun",18,188.0)); // 查询集合中元素,确认去重效果 System.out.println("stus = " + stus); } }
-
打印结果如下:实现了去重
stus = [Student{name='芳芳', age=88, height=158.0}, Student{name='华强', age=19, height=189.0}, Student{name='Ikun', age=18, height=188.0}]
1.4 LinkedHashSet
常用于去重。
1.4.1 概述
LinkedHashSet底层采用的是也是哈希表结构,只不过额外新增了一个双向链表来维护元素的存取顺序。
特点:有序、唯一、无索引。
底层基于哈希表,使用链表记录添加顺序。
1.4.2 数据结构
如下下图所示:
总结:
-
使用HashSet存入自定义对象时,对象所属的类要重写hashCode和equals方法。
每次添加元素,就和上一个元素用双向链表连接一下。第一个添加的元素是双向链表的头节点,最后一个添加的元素是双向链表的尾节点。
1.4.3 演示代码
-
实体Student类
<span style="background-color:#f8f8f8"><span style="color:#aa5500">// 与HashSet中一致</span></span>
-
测试类
<span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheia</span>; <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">LinkedHashSet</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* LinkedHashSet演示</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo03</span> { <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) { <span style="color:#aa5500">// 创建集合对象LinkedHashSet</span> <span style="color:#000000">LinkedHashSet</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span> <span style="color:#000000">stus</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">LinkedHashSet</span><span style="color:#981a1a"><></span>(); <span style="color:#aa5500">// 添加元素</span> <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>,<span style="color:#116644">18</span>,<span style="color:#116644">188.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"华强"</span>,<span style="color:#116644">19</span>,<span style="color:#116644">189.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"芳芳"</span>,<span style="color:#116644">88</span>,<span style="color:#116644">158.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>,<span style="color:#116644">18</span>,<span style="color:#116644">188.0</span>)); <span style="color:#aa5500">// 遍历集合查询元素顺序</span> <span style="color:#770088">for</span> (<span style="color:#000000">Student</span> <span style="color:#000000">stu</span> : <span style="color:#000000">stus</span>) { <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"stu = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">stu</span>); } } }</span>
-
控制台输出结果:证明有序唯一
<span style="background-color:#f8f8f8"><span style="color:#000000">stu</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Student</span>{<span style="color:#000000">name</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'Ikun'</span>, <span style="color:#000000">age</span><span style="color:#981a1a">=</span><span style="color:#116644">18</span>, <span style="color:#000000">height</span><span style="color:#981a1a">=</span><span style="color:#116644">188.0</span>} <span style="color:#000000">stu</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Student</span>{<span style="color:#000000">name</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'华强'</span>, <span style="color:#000000">age</span><span style="color:#981a1a">=</span><span style="color:#116644">19</span>, <span style="color:#000000">height</span><span style="color:#981a1a">=</span><span style="color:#116644">189.0</span>} <span style="color:#000000">stu</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Student</span>{<span style="color:#000000">name</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'芳芳'</span>, <span style="color:#000000">age</span><span style="color:#981a1a">=</span><span style="color:#116644">88</span>, <span style="color:#000000">height</span><span style="color:#981a1a">=</span><span style="color:#116644">158.0</span>}</span>
1.5 TreeSet
使用频率不高,理解为主。
1.5.1 概述
-
元素不可重复,可以使用
TreeSet
实现去重。 -
没有索引。不能通过索引操作元素(通过索引获取、删除、修改等)
-
无法保证存取顺序一致。但是可以将元素按照指定规则进行排序
-
自然排序(
TreeSet
集合默认的排序方式) -
比较器排序
-
1.5.2 存储JDK系统类对象
需求:
-
使用TreeSet存储Integer类型数据
-
使用TreeSet存储String类型数据
演示代码1:存储Integer
-
SetDemo04.java
<span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheia</span>; <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">TreeSet</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* TreeSet存储Integer类型元素</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo04</span> { <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) { <span style="color:#aa5500">// 创建集合对象,指定泛型为Integer</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><</span><span style="color:#008855">Integer</span><span style="color:#981a1a">></span> <span style="color:#000000">nums</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><></span>(); <span style="color:#aa5500">// 添加元素</span> <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">5</span>); <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">1</span>); <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">8</span>); <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">3</span>); <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">6</span>); <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">9</span>); <span style="color:#aa5500">// 打印集合查看元素顺序:验证无序(排序)</span> <span style="color:#aa5500">// nums = [1, 3, 5, 6, 8, 9]</span> <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"nums = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">nums</span>); <span style="color:#aa5500">// 打印集合查看元素顺序:唯一</span> <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">9</span>); <span style="color:#aa5500">// nums = [1, 3, 5, 6, 8, 9]</span> <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"nums = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">nums</span>); <span style="color:#aa5500">// 使用索引获取集合元素,验证无索引</span> <span style="color:#aa5500">// nums.get(1);</span> } }</span>
演示代码2:存储String
-
SetDemo04.java
<span style="background-color:#f8f8f8">package com.itheima; import java.util.TreeSet; /** * TreeSet存储String类型元素 */ public class SetDemo05 { public static void main(String[] args) { // 创建集合对象,指定泛型为String TreeSet<String> strs = new TreeSet<>(); // 添加元素 strs.add("abc"); strs.add("aac"); strs.add("dfg"); strs.add("bba"); strs.add("bag"); strs.add("ccc"); // 打印集合查看元素顺序:验证无序(排序)。字典顺序,自然排序,按照升序排序。 System.out.println("strs = " + strs); // 打印集合查看元素顺序:唯一 strs.add("ccc"); System.out.println("strs = " + strs); // 使用索引获取集合元素,验证无索引 // strs.get(1); } }</span>
1.5.3 TreeSet
排序-自然排序
-
自然排序
元素所属类型实现自然排序接口
Comparable
并重写compareTo
方法后,创建该类对象并存入TreeSet
集合,集合会自动调用元素类的compareTo
方法,决定元素在集合中的顺序。此为自然排序。
自然排序要取元素类实现Comparable
接口,需要做到以下几点:
-
使用空参构造创建
TreeSet
集合 -
元素类(eg:
Student
)需要实现Comparable
接口 -
重写元素类(eg:
Student
)中的compareTo
方法
compareTo
方法返回值与排序结果的关系
-
如果返回值为负数,表示当前存入的元素是较小值,存左边
-
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
-
如果返回值为正数,表示当前存入的元素是较大值,存右边
-
自然排序演示代码:以年龄为条件排序
<span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 让Student类实现Comparable接口,重写compareTo方法,就能实现存入TreeSet后自动排序,这就是自然排序。</span> <span style="color:#aa5500">* 实现接口的时候Comparable的泛型写当前类Student即可</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">Student</span> <span style="color:#770088">implements</span> <span style="color:#000000">Comparable</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span>{ <span style="color:#aa5500">// 其他代码不变,省略不写。</span> <span style="color:#555555">@Override</span> <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">compareTo</span>(<span style="color:#000000">Student</span> <span style="color:#000000">o</span>) { <span style="color:#aa5500">//按照对象的年龄进行排序</span> <span style="color:#008855">int</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">-</span> <span style="color:#000000">o</span>.<span style="color:#000000">age</span>; <span style="color:#770088">return</span> <span style="color:#000000">result</span>; } }</span>
-
测试类代码不变
<span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts</span>; <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">Arrays</span>; <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">TreeSet</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* TreeSet存储自定义类型对象:Student</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo06</span> { <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) { <span style="color:#aa5500">// 创建集合对象,泛型为Student</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span> <span style="color:#000000">stus</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><></span>(); <span style="color:#aa5500">// 创建元素对象并添加</span> <span style="color:#aa5500">// TreeSet不能直接存储自定义对象,因为TreeSet集合不知道对元素进行怎样的排序</span> <span style="color:#aa5500">// ClassCastException: com.itheima.ts.Student cannot be cast to java.lang.Comparable</span> <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"华强"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">187.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"芳芳"</span>, <span style="color:#116644">88</span>, <span style="color:#116644">158.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>)); <span style="color:#aa5500">// 遍历集合中元素,验证set特点</span> <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"stus = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">stus</span>); } }</span>
-
自然排序演示代码:以年龄为主要条件,姓名、身高为辅助条件排序
<span style="background-color:#f8f8f8"><span style="color:#aa5500">// 年龄是主要条件,姓名身高为辅助条件</span> <span style="color:#aa5500">// 如果年龄不同就可以直接排序,年龄相同时才需要借助姓名排序。</span> <span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">mytreeset</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 让Student类实现Comparable接口,重写compareTo方法,就能实现存入TreeSet后自动排序,这就是自然排序。</span> <span style="color:#aa5500">* 实现接口的时候Comparable的泛型写当前类Student即可</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">Student</span> <span style="color:#770088">implements</span> <span style="color:#000000">Comparable</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span>{ <span style="color:#aa5500">// 其他代码</span> <span style="color:#555555">@Override</span> <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">compareTo</span>(<span style="color:#000000">Student</span> <span style="color:#000000">stu</span>) { <span style="color:#aa5500">// 让该方法的返回值和成员变量相关 name age height</span> <span style="color:#008855">int</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#116644">0</span>; <span style="color:#aa5500">// 首先按照年龄比</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">-</span> <span style="color:#000000">stu</span>.<span style="color:#000000">age</span>; <span style="color:#aa5500">// 年龄相同则必身高</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?</span> (<span style="color:#008855">int</span>) (<span style="color:#770088">this</span>.<span style="color:#000000">height</span> <span style="color:#981a1a">-</span> <span style="color:#000000">stu</span>.<span style="color:#000000">height</span>) : <span style="color:#000000">result</span>; <span style="color:#aa5500">// 身高也相同,比名称</span> <span style="color:#770088">return</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?</span> <span style="color:#770088">this</span>.<span style="color:#000000">name</span>.<span style="color:#000000">compareTo</span>(<span style="color:#000000">stu</span>.<span style="color:#000000">name</span>) : <span style="color:#000000">result</span>; } }</span>
-
字符串的
CompareTo
方法字符串
String
同样的实现了Comparable
接口,并重写了CompareTo
方法。方法内部实现的按照字典顺序排序
1.5.4 TreeSet
排序-比较器排序
自然排序高度依赖元素所属类的compareTo
方法,如果排序规则发生要改变,就需要修改元素类的代码,这种设计在经常变换排序规则的环境中不合理。
这时就可以使用比较器排序,解耦排序逻辑和元素实体类。
比较器排序Comparator
的使用步骤:
-
使用
TreeSet
的带参构造创建集合对象 -
创建集合对象时,有参构造传递的参数为
Comparator
(比较器接口)的实现类对象, -
比较器实现类需要重写
compare(To1,To2)
方法
注意:
-
使用比较器排序时,元素类不需要实现任何接口;而是自定义比较器类,并由比较器实现
Comparator
接口
优势:
-
比较排序算法和具体的元素类解耦分离了。
演示代码
-
Student类,不需要实现任何接口
<span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts02</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* HashSet去重原理演示:自定义实体类。不需要实现任何接口</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">Student</span> { <span style="color:#770088">private</span> <span style="color:#008855">String</span> <span style="color:#000000">name</span>; <span style="color:#770088">private</span> <span style="color:#008855">int</span> <span style="color:#000000">age</span>; <span style="color:#770088">private</span> <span style="color:#008855">double</span> <span style="color:#000000">height</span>; <span style="color:#770088">public</span> <span style="color:#000000">Student</span>() { } <span style="color:#770088">public</span> <span style="color:#000000">Student</span>(<span style="color:#008855">String</span> <span style="color:#000000">name</span>, <span style="color:#008855">int</span> <span style="color:#000000">age</span>, <span style="color:#008855">double</span> <span style="color:#000000">height</span>) { <span style="color:#770088">this</span>.<span style="color:#000000">name</span> <span style="color:#981a1a">=</span> <span style="color:#000000">name</span>; <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">=</span> <span style="color:#000000">age</span>; <span style="color:#770088">this</span>.<span style="color:#000000">height</span> <span style="color:#981a1a">=</span> <span style="color:#000000">height</span>; } <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 获取</span> <span style="color:#aa5500">*</span> <span style="color:#aa5500">* @return name</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#008855">String</span> <span style="color:#000000">getName</span>() { <span style="color:#770088">return</span> <span style="color:#000000">name</span>; } <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 设置</span> <span style="color:#aa5500">*</span> <span style="color:#aa5500">* @param name</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">setName</span>(<span style="color:#008855">String</span> <span style="color:#000000">name</span>) { <span style="color:#770088">this</span>.<span style="color:#000000">name</span> <span style="color:#981a1a">=</span> <span style="color:#000000">name</span>; } <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 获取</span> <span style="color:#aa5500">*</span> <span style="color:#aa5500">* @return age</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">getAge</span>() { <span style="color:#770088">return</span> <span style="color:#000000">age</span>; } <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 设置</span> <span style="color:#aa5500">*</span> <span style="color:#aa5500">* @param age</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">setAge</span>(<span style="color:#008855">int</span> <span style="color:#000000">age</span>) { <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">=</span> <span style="color:#000000">age</span>; } <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 获取</span> <span style="color:#aa5500">*</span> <span style="color:#aa5500">* @return height</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#008855">double</span> <span style="color:#000000">getHeight</span>() { <span style="color:#770088">return</span> <span style="color:#000000">height</span>; } <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* 设置</span> <span style="color:#aa5500">*</span> <span style="color:#aa5500">* @param height</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">setHeight</span>(<span style="color:#008855">double</span> <span style="color:#000000">height</span>) { <span style="color:#770088">this</span>.<span style="color:#000000">height</span> <span style="color:#981a1a">=</span> <span style="color:#000000">height</span>; } <span style="color:#555555">@Override</span> <span style="color:#770088">public</span> <span style="color:#008855">String</span> <span style="color:#000000">toString</span>() { <span style="color:#770088">return</span> <span style="color:#aa1111">"Student{"</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"name='"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">name</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">'\''</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">", age="</span> <span style="color:#981a1a">+</span> <span style="color:#000000">age</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">", height="</span> <span style="color:#981a1a">+</span> <span style="color:#000000">height</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">'}'</span>; } } </span>
-
SetDemo07
中创建TreeSet
对象的时候,使用比较器对象指定排序规则<span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts02</span>; <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">Comparator</span>; <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">TreeSet</span>; <span style="color:#aa5500">/**</span> <span style="color:#aa5500">* TreeSet存储自定义类型对象:Student。使用比较器排序</span> <span style="color:#aa5500">*/</span> <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo07</span> { <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) { <span style="color:#aa5500">// 创建集合对象,泛型为Student。并指定比较器对象,在比较器对象中实现排序规则</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span> <span style="color:#000000">stus</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><></span>((<span style="color:#000000">stu1</span>, <span style="color:#000000">stu2</span>) <span style="color:#981a1a">-></span> { <span style="color:#aa5500">// 让该方法的返回值和成员变量相关 name age height</span> <span style="color:#008855">int</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#116644">0</span>; <span style="color:#aa5500">// 首先按照年龄比</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">stu1</span>.<span style="color:#000000">getAge</span>() <span style="color:#981a1a">-</span> <span style="color:#000000">stu2</span>.<span style="color:#000000">getAge</span>(); <span style="color:#aa5500">// 年龄相同则必身高</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?-</span> (<span style="color:#008855">int</span>) (<span style="color:#000000">stu1</span>.<span style="color:#000000">getHeight</span>() <span style="color:#981a1a">-</span> <span style="color:#000000">stu2</span>.<span style="color:#000000">getHeight</span>()) : <span style="color:#000000">result</span>; <span style="color:#aa5500">// 身高也相同,比名称</span> <span style="color:#770088">return</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?</span> <span style="color:#000000">stu1</span>.<span style="color:#000000">getName</span>().<span style="color:#000000">compareTo</span>(<span style="color:#000000">stu2</span>.<span style="color:#000000">getName</span>()) : <span style="color:#000000">result</span>; }); <span style="color:#aa5500">// 创建元素对象并添加</span> <span style="color:#aa5500">// TreeSet不能直接存储自定义对象,因为TreeSet集合不知道对元素进行怎样的排序</span> <span style="color:#aa5500">// ClassCastException: com.itheima.ts.Student cannot be cast to java.lang.Comparable</span> <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"华强"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">187.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"芳芳"</span>, <span style="color:#116644">88</span>, <span style="color:#116644">158.0</span>)); <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>)); <span style="color:#aa5500">// 遍历集合中元素,验证set特点</span> <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"stus = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">stus</span>); } }</span>
1.5.5 两种排序方式对比
不同:实现排序的方式
-
自然排序:元素实体类实现
Comparable
接口,重写compareTo
方法,根据返回值进行排序。 -
比较器排序:创建
TreeSet
对象的时候传递Comparator
的实现类对象,重写compare
方法,根据返回值进行排序。 -
应用场景:在使用的时候,默认使用自然排序;当自然排序不满足现在的需求时,使用比较器排序
相同:排序的底层逻辑
compareTo/compare
方法返回值与排序结果的关系
-
如果返回值为负数,表示当前存入的元素是较小值,存左边
-
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
-
如果返回值为正数,表示当前存入的元素是较大值,存右边
-
不需要记忆,随便写谁在前在后无所谓,运行之后如果顺序对了,就不用改了;顺序不对,前面加个-号,或者改下顺序即可。
1.6 单列集合汇总
-
单列集合顶层父接口:Collection
-
内部规定了单列集合通用的功能:添加元素、获取长度、(删改查)
-
-
分为两个分支
-
List:通用特点:有序(存取顺序一致)、可重复,有索引
-
ArrayList:底层是数组,查询快,增删慢
-
LinkedList:底层是双向链表,查询慢、增删快
-
-
Set:通用特点:无序(存取顺序不一致)、唯一、无索引
-
HashSet:底层是Hash表,增删改查性能均衡
-
JDK8以前是数组+链表
-
JDK8及以后是数组+链表+红黑树
-
-
LinkedHashSet:底层是Hash表+双向链表记录顺序,所以有序,同样唯一、无索引
-
TreeSet:底层是红黑树,可以排序,唯一,无索引
-
图示:
-
==2. 可变参数==
2.1 概述
-
可变参数:在方法定义时可以指定个数不确定的参数,对于同一方法可以使用不同个数的实参调用。
eg:定义个
print(某个类型 可变参数)
方法,可以通过以下方式调用print("hello"); print("hello","lisi"); print("hello","zhangsan", "vsunks") ......
-
格式:
参数一般在方法定义和调用时使用,可变参数一般用作形参(方法定义位置)
-
格式:数据类型... 变量名
-
范例:
int... num
-
放在方法签名上:修饰符 返回值类型 方法名(数据类型... 变量名){}
-
范例:
public static int sum(int... num){}
-
代码演示:
-
KbArgsDemo01.java
package com.itheima; /** * 可变参数入门案例 */ public class KbArgsDemo01 { public static void main(String[] args) { // 2. 调用sum方法,使用可变参数 // 2.1 调用方式1:准备一个元素类型和可变参数类型一致的数组 int[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int sum = sum(ints); System.out.println("sum = " + sum); // 2.2 调用方式2:传递多个散列的值/变量,值/变量的类型与可变参数一致即可; // 会自动将这几个散列的实参封装到可变参数中 System.out.println("sum(1,2,3,4,5) = " + sum(1, 2, 3, 4, 5)); } // 1. 定义方法,并在方法形参位置使用可变参数 // 格式:数据类型... 变量名 public static int sum(int... nums) { int sum = 0; // 1.1 在方法内部,可变参数可以当做数组来使用 for (int num : nums) { sum += num; } return sum; } }
-
2.2 注意事项:
-
可变参数本质就是一个数组。
-
可变参数只能作为最后一个形参
-
可变参数相当于一个大胖子,吃实参,有一个吃一个;如果放在前面,调用方法时,传递的所有实参都会被可变参数吃掉。
-
-
演示代码
package com.itheima; /** * 可变参数注意事项 * * @Author Vsunks.v * @Date 2023/3/2 11:48 * @Blog blog.sunxiaowei.net/996.mba * @Description: 可变参数注意事项 */ public class KbArgsDemo02 { public static void main(String[] args) { // 2. 调用sum方法,使用可变参数 // 2.1 调用方式1:准备一个元素类型和可变参数类型一致的数组 int[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int sum = sum(0, ints); System.out.println("sum = " + sum); // 2.2 调用方式2:传递多个散列的值/变量,值/变量的类型与可变参数一致即可; // 会自动将这几个散列的实参封装到可变参数中 System.out.println("sum(1,2,3,4,5) = " + sum(1, 2, 3, 4, 5)); } // 1. 定义方法,可变参数放在最后 public static int sum(int num0, int... nums) { int sum = num0; // 1.1 在方法内部,可变参数可以当做数组来使用 for (int num : nums) { sum += num; } return sum; } // 1. 定义方法,可变参数放在前面 // Vararg parameter must be the last in the list 可变参数必须是列表的最后一个 /* public static int sum(int... nums, int num0) { int sum = num0; // 1.1 在方法内部,可变参数可以当做数组来使用 for (int num : nums) { sum += num; } return sum; } */ }
-
2.3 应用场景
可变参数的本质就是数组(容器)。
可变参数之于数组,在更少的场景可以更简单的使用数组这个容器。
-
更少的场景:方法形参中最后一个位置的数组可以使用可变参数替换;在方法中可以把他当做数组使用。
-
更简单使用:调用方法时,不需要创建数组对象并传递,而是直接传递多个散列的实参。
-
形参上有数组,调用的时候想方便(不想把多个数据封装成一个数组再传递),就可以使用可变参数。
应用场景举例:
-
JDK9
中添加了众多快捷创建集合的方法。 -
Collections工具类中,有些方法也用到了可变参数。
-
3. Collections
3.1 概述
Collections是Collection单列集合的工具类,内部封装了一些操作单列集合的方法。
该工具类中用到了可变参数。
经验分享:
-
在Java中,xxxs多是xxx的工具类。eg:Arrays是操作数组的工具类,Collections是操作Collection的工具类
-
3.2 成员方法
Collections中常见成员方法如下:
方法签名 方法说明 public static <T> boolean addAll(Collection c, T... e) 向集合批量添加元素 public static void shuffle(List<?> list) 打乱List集合中元素顺序 public static <T> void sort(List<T> list) 对List集合中元素排序(自然排序) public static <T> void sort(List<T> list, Comparator c) 对List集合中元素排序(比较器排序) 注释:
// 1.public static <T> boolean addAll(Collection<? super T> c, T...e) 向集合批量添加元素 // 2.public static void shuffle(List<?> list):打乱List集合中元素顺序 // 3.public static <T> void sort(List<T> list): 对List集合中元素排序(自然排序) // 4.public static <T> void sort(List<T> list, Comparator c) 对List集合中元素排序(比较器排序)
演示代码:
-
CollectionsDemo.java
import com.itheima.Student; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; /** * Collections工具类演示 */ public class CollectionsDemo { public static void main(String[] args) { // 1.public static <T> boolean addAll(Collection<? super T> c, T...e) 向集合批量添加元素 ArrayList<String> names = new ArrayList<>(); Collections.addAll(names,"Ikun","芳芳","芳芳的Ikun","Ikun真爱芳芳"); System.out.println("names = " + names); // 2.public static void shuffle(List<?> list):随机打乱List集合中元素顺序 Collections.shuffle(names); System.out.println("names = " + names); ArrayList<Student> stus = new ArrayList<>(); stus.add(new Student("Ikun",18,188.0)); stus.add(new Student("华强",19,189.0)); stus.add(new Student("芳芳",88,158.0)); stus.add(new Student("Ikun",18,188.0)); System.out.println("stus = " + stus); // 3.public static <T> void sort(List<T> list): 对List集合中元素排序(自然排序) Collections.sort(stus); System.out.println("stus = " + stus); System.out.println("stus = " + stus); // 元素本身具有比较能力时,如果指定了比较器,优先使用比较器的规则。 // 4.public static <T> void sort(List<T> list, Comparator c) 对List集合中元素排序(比较器排序) Collections.sort(stus, (stu1, stu2) -> { // 让该方法的返回值和成员变量相关 name age height int result = 0; // 首先按照年龄比 result = -stu1.getAge() + stu2.getAge(); // 年龄相同则必身高 result = result == 0 ?- (int) (stu1.getHeight() - stu2.getHeight()) : result; // 身高也相同,比名称 return result == 0 ? stu1.getName().compareTo(stu2.getName()) : result; }); System.out.println("stus = " + stus); } }
-
3.3 案例:斗地主
需求:
一副牌:54张
三个人斗地主。
- 元素实体类Student.java,实现可比较接口,重写compareTo方法
package com.itheima; /** * Collections排序:自然排序 */ public class Student implements Comparable<Student> { private String name; private int age; private double height; public Student() { } public Student(String name, int age, double height) { this.name = name; this.age = age; this.height = height; } /** * 获取 * * @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; } /** * 获取 * * @return height */ public double getHeight() { return height; } /** * 设置 * * @param height */ public void setHeight(double height) { this.height = height; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}'; } @Override public int compareTo(Student stu) { // 让该方法的返回值和成员变量相关 name age height int result = 0; // 首先按照年龄比 result = this.age - stu.age; // 年龄相同则必身高 result = result == 0 ? (int) (this.height - stu.height) : result; // 身高也相同,比名称 return result == 0 ? this.name.compareTo(stu.name) : result; } }
- -
-
-