day07 【Collections & Set 集合】
今日内容介绍
Collections工具类
增强for循环
泛型
红黑树
Set集合
第一章 Collections 工具类【重点】
1.1 Collections 工具类的基本使用
package com.itheima.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class CollectionsDemo01 {
/*
Collections 是单列集合的工具类
特点 构造私有,方法都是静态的,直接通过类名调用。
T... elements 你想传几个数据 就传几个
static <T> boolean addAll(Collection<? super T> c, T... elements)
将所有指定元素添加到指定 collection 中。
*/
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
// 向集合中添加 多个元素
Collections.addAll(list,"楚人美","范德彪","乔布斯","马大帅");
System.out.println("查看集合中的元素:"+list);
// static void reverse(List<?> list) 反转指定列表中元素的顺序。
Collections.reverse(list);
System.out.println("查看集合reverse之后里面的元素:"+list);
// static void shuffle(List<?> list) 打乱集合中的顺序
Collections.shuffle(list);
System.out.println("查看打乱顺序的集合:"+list);
// static <T> void sort(List<T> list) T类型一定是可排序,它一定是符合排序操作的
// 要实现一个接口 Comparable 比较接口
// 根据元素的自然顺序 对指定列表按升序进行排序
Collections.sort(list);
System.out.println("查看集合排序之后里面的元素:"+list);
}
}
1.2 基本的排序与修改排序规则操作
package com.itheima.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class CollectionsDemo02 {
/**
* 讲解 排序功能
* static <T > void sort(List<T> list)
* 根据元素的自然顺序 对指定列表按升序进行排序
* 集合里面元素必须实现 Comparable接口
* 如果没实现不能排序
*
* @param args
*/
public static void main(String[] args) {
//创建集合
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,100,50,20,1000,10,99);
System.out.println("集合没有排序前:"+list);
//进行排序
Collections.sort(list);
System.out.println("排序后的集合:"+list);
//默认排序是升序
//不满意 变成降序?
/*
static <T> void sort(List<T> list, Comparator<? super T> c)
根据指定比较器产生的顺序对指定列表进行排序。
*/
Collections.sort(list,
// 这个接口就是体现了对 规则的定义,具体的规则由我们重写实现。
new Comparator<Integer>() {//实现该接口就不再使用Integer默认排序规则,而是使用新的规则进行排序
@Override
public int compare(Integer o1, Integer o2) {//重写的排序方法
return o2-o1; // 前-后 升序 后-前 降序
}
});
System.out.println("最后排序后的集合:"+list);
}
}
1.3 Collections 排序自定义规则按照学生年龄排序
package com.itheima.collections;
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.itheima.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class CollectionsDemo03 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("jack",18));
list.add(new Student("rose",28));
list.add(new Student("abc",16));
System.out.println("集合排序前 按照存的顺序:"+list);
/*
对集合里面的 学生 进行排序 要求 排序规则 根据学生的 年龄 降序排列!
*/
// Collections.sort(list);list集合中的元素 student Student类型没有实现Comparable 接口
// 不具备排序规则
/*
怎么让我们的学生对象可以完成排序
1: 去上Student类型 实现 Comparable接口 comparTo方法 里面去实现 排序的规则。
第一种 会把排序规则写死在类中。
2: 直接使用 Collections.sort(list,Comparator接口)
不想把规则写死 ,每次排序我都自定义排序的规则,规则不会影响类在其他地方的使用。
需要在 第二参数上 实现该比较接口 即可
---- 匿名内部类 你根据提示回车
*/
Collections.sort(list, new Comparator<Student>() {
//我们现在来给Student 实现一个排序的新规则
@Override
public int compare(Student o1, Student o2) {//方法就是实现新的规则的
// 前-后 升序 后-前 降序
return o2.getAge()-o1.getAge();//降序 根据学生的 年龄 降序排列!
// o2.getName().compareTo(o1.getName()) 根据姓名进行降序
}
});
System.out.println("排序后:"+list);
}
}
第二章 泛型【重点】
2.1 泛型的好处
没有泛型的代码
public class GenericDemo01 {
public static void main(String[] args) {
Collection coll = new ArrayList();
//添加元素
coll.add("itcast");
coll.add("itheima");
coll.add(5);//因为加的是Integer类型 自动装箱了
// 没有泛型默认是 Object类型
for (Object o : coll) {
// 将Object类型向下 转型 转换成 String 因为String 可以看字符串的长度
String s = (String)o;
System.out.println("字符串的长度:"+s.length());
}
/*
ClassCastException: 类型转换异常,前面两个元素 向下转型成 String,是OK的没毛病。
第三个元素 是Integer类型, 它和String没关系,不能强制的进行转换。
这种问题 是不是在运行期才暴露出来。
编译没有爆出来!!
把这种问题 提前的解决,这样运行时就没问题了。
而且我们一般 一个容器中 只存储相同的数据类型。
如果没有泛型,什么类型都可以存储,因为什么类型都是Object.
泛型的出现解决了两件事情
泛型的好处
1: 将运行期的异常,提前到了编译语法阶段
2: 避免了强转的麻烦。
3:一旦指定了泛型,那么集合中数据在编译期就进行了统一。
4: 泛型可以实现模板化,把数据类型当做参数传递。
*/
}
}
加了泛型 解决了 问题
package com.itheima.generic;
import java.util.ArrayList;
import java.util.Collection;
public class GenericDemo02 {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<String>();
//添加元素
coll.add("itcast");
coll.add("itheima");
// coll.add(5);//因为加的是Integer类型 自动装箱了
// 没有泛型默认是 Object类型
for (String o : coll) {
System.out.println("字符串的长度:"+o.length());
}
}
}
泛型的概念
泛型:可以在类或方法中预支地使用未知的类型。(未知 现在不知道 未来会知道)
泛型的应用
泛型可以定义在 类 方法 接口上 等等
作用:相当于一个类的模板
package com.itheima.generic;
import java.util.ArrayList;
public class GenericDemo03 {
/**
* 泛型是什么?
* 存在于源码阶段,它表示不确定的类型,在我们看来就是一个未知,未来(使用)会得知的类型。
* 特征:
* 将来 传什么是什么!
* 定义在哪:
* 类 方法 接口上 等等
* public class ArrayList<E> String
*
* add(E e) String
*
* Iterator<E> iterator()
定义的时候 确定了 泛型类型吗?
*
* E 只是一个代称 在 ArrayList这个E 代表 集合元素的类型,但是是什么不知道。
* 现在源代码阶段 有没有具体的 E 类型 ,没有的
*
* 泛型是一个 未知的类型,未来的类型
* 特征: 将来 传什么 是什么。
ArrayList<String> list = new ArrayList<>();
list.add(String s)
*
* 将来使用的时候 一定是一个具体的类型。
*
* 为什么定义这个呢?
* 因为 在一些操作过程中,我们会遇到不确定的类型,这种不确定的类型,在源码阶段 就先使用 泛型替代。
* 需要注意的是 一旦程序运行了,泛型作用也就失效了。--- 泛型在运行期是被擦除的。
*/
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
}
}
2.2 泛型类的定义【重点】
定义泛型类的目的
1:创建一个模板,减少了相似类的创建。---高内聚的体现。
2: 在我不确定具体使用什么的时候,我可以先以泛型形式,在使用的确定。
研究
定义格式:
修饰符 class 类名<代表泛型的字符串>{}
比如:
public class 类名<T> {
...
}
什么时候确定泛型的具体类型:
在创建对象的时候确定泛型。
package com.itheima.generic02;
/**
* 定义格式:
* 修饰符 class 类名<>{}
* 不要写具体的类型。
* 泛型在定义的时候不具体,在使用的时候才变得具体。
*
* 1:那写什么呀,
* 随便,官方用到过E 在 集合中 ArrayList<E> E 是 element的缩写,因为定义这个泛型的含义 是存储到集合中的元素。
* 2:尖括号中可以写几个
* 需要几个写几个。
* 但是建议
* 见名知意
*
*
*/
public class MyClass01<MVP>{//MVP代表未知,将来传什么就是什么
//是因为 你在使用类的时候 这个MVP 一定是一个具体的类型了,
private MVP mvp;
public MVP getMVP(){
return mvp;
}
public void setMVP(MVP mvp){
this.mvp=mvp;
}
}
package com.itheima.generic;
public class GenericDemo03Class {
public static void main(String[] args) {
//泛型定义在类 创建对象的时候确定
//使用时候给具体类型
MyClass01<String> myclass = new MyClass01<>();
myclass.setMvp("aaa");
String mvp = myclass.getMvp();
MyClass01<Integer> myclass2 = new MyClass01<>();
myclass2.setMvp(1);
Integer mvp1 = myclass2.getMvp();
}
}
2.3 泛型方法
泛型方法定义格式:
修饰符 <定义方法上的泛型> 返回值 方法名(参数列表){}
刚刚在方法上定义的泛型,可以在方法的参数列表上使用。
举例:
修饰符 static <M> 返回值类型 方法名称(M t) {
...
}
还可以用在返回值上
什么时候确定泛型:
调用方法的时候,传什么类型,泛型就是什么类型。
package com.itheima.generic02;
public class MyClass02<T> {//泛型定义在类上面
// 此方法上的泛型 是类上面定义的 是创建对象的时候就已经确定了。
public void print(T t ){
System.out.println(t);
}
/**
* 泛型定义在 方法上的格式
* 修饰符 <定义方法上的泛型> 返回值 方法名(参数列表){}
*
*
*/
public <E> void show(E e){
System.out.println("获取泛型的具体类型:"+e.getClass());
}
}
package com.itheima.generic02;
import com.itheima.collections.Student;
public class Demo02 {
public static void main(String[] args) {
MyClass02<String> myClass02 = new MyClass02<>();
myClass02.show(new Student());
}
}
2.4 泛型接口
泛型: jdk1.5 添加的新特性
泛型接口: 看 MyClass05 定义接口时,该接口中需要处理某种类型的数据,但是什么类型,不确定,所以定义成泛型 泛型变量一般用大写字母表示: T(Type),E(Element),K(Key),V(Value)
定义接口的定义格式:
public interface 接口名称<泛型变量> {
...
}
举例:
public interface 接口名称<T> {
...
}
接口上定义的泛型,什么时间确定具体的类型呢?
1.实现类实现接口时,确定接口上泛型的具体类型的话, 直接指定具体类型
public class MyClass03 implements MyInter<Integer>{
2.定义实现类时,也不确定接口上的泛型 该实现类必须定义为泛型类 而且实现类上的泛型和接口上的泛型要保持一致 创建实现类对象时,确定具体的类型
/**
* 现在有个接口
* 泛型定义格式
* 修饰符 interface 接口名<泛型>{}
*/
public interface MyInter01<T> {
public abstract void add(T t);
}
package com.itheima.generic02;
/*
泛型定义在接口上
什么时候 确定泛型类型呢
1:定义类实现接口的时候,确定。
*/
public class MyInterImplA implements MyInter01<String> {
@Override
public void add(String s) {
}
}
package com.itheima.generic02;
/*
泛型定义在接口上
什么时候 确定泛型类型呢
1:定义类实现接口的时候,确定。
2: 定义类实现接口的时候,没有确定,
那么就在 创建对象的时候完成确定泛型类型。
*/
public class MyInterImplB<T> implements MyInter01<T> {
@Override
public void add(T t) {
}
}
package com.itheima.generic02;
import java.util.ArrayList;
public class Demo03 {
public static void main(String[] args) {
MyInterImplB<String> myInterImplB = new MyInterImplB<>();
ArrayList<String> list = new ArrayList<>();
}
}
2.5 泛型通配符
概述
我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:
?表示不确定的 java 类型
T (type) 表示具体的一个 java 类型
K V (key value) 分别代表 java 键值中的 Key Value
E (element) 代表 Element
? 无界通配符
接下来讲一个简单的使用,但实际上它是用于进行边界处理的。
package com.itheima.generic03;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add("xyz");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(100);
list2.add(200);
printList(list);
printList(list2);
LinkedList<String> list3 = new LinkedList<>();
list.add("abc");
list.add("xyz");
printList(list3);
}
//定义一个方法 遍历打印集合中的数据
/**
* 如果 这里写了泛型
* 泛型 这块应该写什么?
*
* 在方法参数上,如果不确定 将来传递的泛型是什么类型
* 1:不写泛型,去泛型化。
* 2:另外一种 使用 通配符 ? 来表示 未知
* ?叫做 未知通配符
* 未知通配符方法中的代码 也是按照默认Object接收。
*
* ArrayList<String> list
* ArrayList<Integer> list2
* LinkedList<String> list3
*
* 类型
* ArrayList<String> 整体是一个类型
* ArrayList<Integer> 整体是一个类型
*
* List<String> list= new ArrayList<String>()
* List<Integer> list= new ArrayList<Integer>()
* List<Object> list= new ArrayList<Object>()
*
* 下面两种是错误的!!
* List<Object> list= new ArrayList<String>()
* ArrayList<Object> list= new ArrayList<String>()
*/
public static void printList(List<?> list){
for (Object o : list) {
System.out.println(o);
}
}
}
2.6 泛型的上限(上界通配)
?extends 概念:
- 用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
- 用法:
< ? extends E>
相当于设置了泛型向上的界限,最大是 E 类型,还可以是其子类。
注意:
- 如果传入的类型不是 E 或者 E 的子类,编译不成功
- 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
/*
父类: Person
子类: Worker
子类: Teacher
子类: JavaTeacher
一个父类的子类可以有任意多个,如何表示出一个父类的任意子类呢?
?: 任意一种引用类型
? extends Person: 表示Person类型或者Person类型的任意子类型
? extends E: 表示E类型或者E类型的任意子类型
泛型的上限:
? extends Person: 表示Person类型或者Person类型的任意子类型
? extends E: 表示E类型或者E类型的任意子类型
*/
package com.itheima.generic03;
import java.util.ArrayList;
import java.util.List;
public class Demo02Extends {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("zs", 18));
list1.add(new Person("ls", 28));
list1.add(new Person("ww", 38));
ArrayList<Worker> list2 = new ArrayList<>();
list2.add(new Worker("zs01", 18));
list2.add(new Worker("ls01", 28));
list2.add(new Worker("ww01", 38));
ArrayList<Teacher> list3 = new ArrayList<>();
list3.add(new Teacher("zs02", 18));
list3.add(new Teacher("ls02", 28));
list3.add(new Teacher("ww02", 38));
ArrayList<String> list4 = new ArrayList<>();
list4.add("aaa");
list4.add("bbb");
ArrayList<Integer> list5 = new ArrayList<>();
list5.add(100);
list5.add(200);
ArrayList<Object> list6 = new ArrayList<>();
list6.add(new Object());
printList(list1);
printList(list2);
printList(list3);
// printList(list4);
// printList(list5);
// printList(list6);
}
/*
打印方法
设定泛型的上限!
设置 最大的
需求 只允许 集合里面的类型是 Person以及Person的子类
Person的子类
extends Person
*/
public static void printList(List<? extends Person> list){
for (Object o : list) {
System.out.println(o);
}
}
}
2.7 泛型的下限(下界通配)
? super 概念:
- 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object。
- 用法:
< ? super E>
相当于设置了泛型向下的界限,最小是 E 类型,还可以是其父类。
注意:
- 在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
/*
//使用3.7中定义的Person/Worker/Teacher/JavaTeacher类
子类: JavaTeacher
父类: Teacher
父类: Worker
父类: Person
一个子类的父类可以有任意多个,如何表示出一个子类的任意父类呢?
?: 任意一种引用类型
? super JavaTeacher: 表示JavaTeacher类型或者JavaTeacher类型的任意父类型
? super E: 表示E类型或者E类型的任意父类型
泛型的下限:
? super JavaTeacher: 表示JavaTeacher类型或者JavaTeacher类型的任意父类型
? extends E: 表示E类型或者E类型的任意父类型
*/
package com.itheima.generic03;
import java.util.ArrayList;
import java.util.List;
public class Demo03 {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("zs", 18));
list1.add(new Person("ls", 28));
list1.add(new Person("ww", 38));
ArrayList<Worker> list2 = new ArrayList<>();
list2.add(new Worker("zs01", 18));
list2.add(new Worker("ls01", 28));
list2.add(new Worker("ww01", 38));
ArrayList<Teacher> list3 = new ArrayList<>();
list3.add(new Teacher("zs02", 18));
list3.add(new Teacher("ls02", 28));
list3.add(new Teacher("ww02", 38));
ArrayList<String> list4 = new ArrayList<>();
list4.add("aaa");
list4.add("bbb");
ArrayList<Integer> list5 = new ArrayList<>();
list5.add(100);
list5.add(200);
ArrayList<Object> list6 = new ArrayList<>();
list6.add(new Object());
printList(list1);
// printList(list2);
printList(list3);
// printList(list4);
// printList(list5);
printList(list6);
}
/*
打印方法
设置泛型的下限
设置最小 传的参数类型只能比它大
最小设置 Teacher
最小是Teacher 里面可以填 Teacher以及Teacher的父类
super来表示父类引用
? super Teacher 泛型只能传Teacher 以及 Teacher的父类
*/
public static void printList(List<? super Teacher> list){
for (Object o : list) {
System.out.println(o);
}
}
}
第三章 红黑树数据结构【了解】
红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。
红黑树能够以O(log2n)的时间复杂度进行搜索、插入、删除操作。
此外,由于它的设计,任何不平衡都会在三次旋转之内解决。
当然,还有一些更好的,但实现起来更复杂的数据结构 能够做到一步旋转之内达到平衡,
但红黑树能够给我们一个比较“便宜”的解决方案。
第四章 Set 集合
4.1 Set 集合的特点
* set集合特点
元素唯一:不能重复 (equals方法不为true)
无序:为了保证元素的唯一,所以没有关系是否有序。
不保证存取顺序一致。
4.2 Set 集合的基本使用
package com.itheima.set;
import java.util.HashSet;
public class HashSetDemo {
/**
* set集合特点
* 元素唯一 什么是唯一?字符串内容唯一。
* 一会儿研究 为什么元素会唯一。
* 无序:存取顺序不一致。
*
*/
public static void main(String[] args) {
//创建 set集合对象
HashSet<String> set = new HashSet<>();
//存数据
set.add("nba");//进行hacode计算 108845 存
set.add("cba");// 进行hacode计算 98274 存
set.add("cca");//98305 存
set.add("cba");// 进行hacode计算 98274 哈希冲突 进行 equals比较 true 不存
set.add(new String("cba"));//98274 哈希冲突 进行 equals比较 true 不存
set.add("cuba");//存 不一样 109999
;
System.out.println("集合的长度:"+set.size());
//遍历
for (String s : set) {
System.out.println(s);
}
}
}
4.3 hashCode 方法
4.5 哈希表的结构
总结:
1.哈希值不同,能否说明内容一定不同?
肯定的,必须的
2.哈希值相同,能否说明内容一定相同?
不能的
继续调用equals方法
返回false: 内容不相同
返回true: 内容相同
改口:
以前调用toString方法,说返回的是对象的地址值
但本质是对象的哈希值
4.6 HashSet 存储元素的原理
/*
HashSet集合存储元素的过程
1.计算哈希值,使用哈希值%数组长度,计算在数组中存储的索引
2.判断该索引下是否有元素
3.没有元素: 直接存储
4.如果有元素:
调用equals方法
true: 不存储
false: 存储
HashSet集合保证元素唯一: 依赖hashCode方法和equals方法
要求:
HashSet集合存储对象所属的类要覆盖重写hashCode方法和equals方法
*/
public class Demo06HashSet {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("abc");//96354
set.add("重地");//1179395
set.add("通话");//1179395
set.add("abc");//96354
System.out.println("abc".hashCode());
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
System.out.println(96354%16);
System.out.println(1179395%16);
}
}
4.7 HashSet 存储自定义对象
package com.itheima.set;
import java.util.Objects;
public class Student implements Comparable<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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);//重写的哈希地址值 是根据 对象中 name 属性 和age属性 进行计算而来的 !!跟原来的new的地址没关系
}
@Override
public int compareTo(Student other) {
return this.age-other.age;// this 当前的学生对象 other 其他的学生对象 当前和其他进行比较 如果是正数 就交换位置
}
}
package com.itheima.set;
import java.util.HashSet;
public class Demo {
public static void main(String[] args) {
//几个学生对象
Student s1 = new Student("潘子",61);
Student s2 = new Student("嘎子",31);
Student s3 = new Student("柳岩",36);
Student s4 = new Student("潘子",61);
HashSet<Student> set = new HashSet<>();
set.add(s1);//0x11 存 更新之后 根据name和age进行一轮哈希运算 得到的结果 28118022 存
set.add(s2);//0x22 存 21896478 存
set.add(s3);//0x33 存 26309519 存
set.add(s4);//0x44 存 28118022 出现了哈希碰撞 比较 属性成员 name一样 age一样 返回true 不添加
System.out.println("set集合长度:"+set.size());
for (Student student : set) {
System.out.println(student);
}
}
}
4.8 LinkedHashSet 集合的使用
package com.itheima.set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
public class LinkedHashSetDemo {
public static void main(String[] args) {
//创建 set集合对象
LinkedHashSet<String> set = new LinkedHashSet<>();
//存数据
set.add("nba");
set.add("cba");
set.add("cca");
set.add(new String("cba"));
set.add("cuba");//存 不一样 109999
;
System.out.println("集合的长度:"+set.size());
//遍历
for (String s : set) {
System.out.println(s);
}
}
}