泛型
(1)概念:表示集合中存储的数据类型
(2)好处:
a)把运行时期的问题提前到了编译期间
b)避免了强制类型转换(如果不添加泛型,集合中存储的数据是Object[]数组,在调用一些方法的时候就需要进行强制类型转换)
(3)使用位置
类后面:成为泛型类
方法申明上:泛型方法
接口后面:泛型接口
(4)定义格式
<类型> 类型可以用任意字母代表
(5)注意事项:
对于泛型而言,属于编译时,编译时看左边,运行时看左边,看等号左边是谁,则优先使用谁
泛型类
(1)定义格式
修饰符 class 类名<泛型类型>
示例:
//泛型类
public class Box<SVIP> {
//定义泛型
private SVIP vip;
public SVIP getVip() {
return vip;
}
public void setVip(SVIP vip) {
this.vip = vip;
}
}
泛型方法
(1)定义格式:
修饰符 <泛型类型> 返回值类型 方法名(类型 变量名){ }
示例
public <T> void show(T t){
}
泛型接口
(1)定义格式
修饰符 interface 接口名<泛型类型>{}
示例:
public interface intel<T> {
}
//泛型接口 ---> 相当于是 List
public interface JieKou<SVIP> {
//定义抽象方法
public abstract void methodAbsract(SVIP vip);
}
//接口实现类 ---> 相当于是 ArrayList
public class ShiXianLei<SVIP> implements JieKou<SVIP>{
@Override
public void methodAbsract(SVIP vip) {
System.out.println(vip);
System.out.println(vip.getClass());
}
}
//测试类
public class Test {
public static void main(String[] args) {
// (1). 创建实现类的对象,定义泛型是 String
ShiXianLei<String> sxl1 = new ShiXianLei<>();
sxl1.methodAbsract("Hello");
System.out.println("----------");
// (2). 创建实现类的对象, 定义泛型是 Float
ShiXianLei<Float> sxl2 = new ShiXianLei<>();
sxl2.methodAbsract(6.666F);
}
}
类型通配符<?>
(1) 类型通配符: <?>
ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型, 但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
(2) 类型通配符上限: <? extends 类型>
ArrayList<? extends Number>: 它表示的类型是Number或者其子类型
(3) 类型通配符下限: <? super 类型>
ArrayList<? super Number>: 它表示的类型是Number或者其父类型
(4)示例:
前提条件:
class Integer extends Number{ .... }
class Number extends Object{ .... }
定义泛型的上限限定 extends
ArrayList<? extends Number> //如果这样写,表示哪些可以使用啊? Number 和 Number 的子类。 (Number\Integer)
定义泛型的下限限定 super
ArrayList<? super Number> //如果这样写,表示哪些可以使用啊? Number 和 Number 的父类。 (Number\Object)
例如: Java的底层代码 API 当中,有这样的实例。
在 Java底层代码, 集合体系结构 Collection 当中,包含有一个方法:
boolean addAll(Collection<? extends E> c); //定义的是泛型的 上限限定。
(5)坑
//泛型通配符的练习题
public class Test {
public static void main(String[] args) {
//采用普通的方式创建对象
ArrayList<String> list1 = new ArrayList<>();
method(list1); //...【2】ArrayList...
System.out.println("-------");
//采用多态的方式创建对象
Collection<String> list2 = new ArrayList<>();
method(list2); //...【1】Collection...
System.out.println("=========");
Set<Integer> set1 = new HashSet<>();
method(set1); //...【3】Set...
System.out.println("----");
HashSet<Double> set2 = new HashSet<>();
method(set2); //...【4】HashSet...
}
//对于泛型而言,将运行时的问题,提前到了编译时。 ---> 泛型属于编译时。
//多态是: 对于成员方法而言, 编译看左边,运行看右边。 --》因为方法有重写。
//对于泛型而言,属于编译时。 编译看左边, 运行看左边。
// 看等号的左边是谁,则优先使用谁。
public static void method(Collection<?> param){
System.out.println("...【1】Collection...");
}
public static void method(ArrayList<?> param){
System.out.println("...【2】ArrayList...");
}
public static void method(Set<?> param){
System.out.println("...【3】Set...");
}
public static void method(HashSet<?> param){
System.out.println("...【4】HashSet...");
}
}
Set集合
(1)分类
HashSet:常用于去除重复
TreeSet:常用于排序
(2)特点
a) 无序:存放和取出的顺序可能不一致
b) 无重复:不可以有重复的元素
c) 无索引:没有带索引的方法
TreeSet
(1)特点
a) 不包含重复元素
b) 没有带索引的方法
c) 可以按照规则进行排序
(2)注意事项
a) 如果存储自定义类型数据,必须要制定比较规则
aa) 自然排序,需要实现Comparable接口
升序:this-o
降序:o-this
示例:
// 学生类
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 int compareTo(Student student) {
// 从小到大 this-student; 从大到小 student-this
int result = this.age-student.age;
// 如果年龄相同,需要按照姓名排
result = (result==0)? this.name.compareTo(student.name):result;
return result;
}
}
// 测试类
public class Test {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>();
ts.add(new Student("zhangsan",19));
ts.add(new Student("lisi",15));
ts.add(new Student("wangwu",21));
ts.add(new Student("zhaoliu",15));
ts.forEach(student -> System.out.println(student));
}
}
bb) 比较器排序,不需要实现Comparable接口
升序:o1-o2
降序:o2-o1
示例:
// 学生类
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 +
'}';
}
}
// 测试类
public class Test {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student student, Student t1) {
int result = student.getAge() - t1.getAge();
result = result==0?student.getName().compareTo(t1.getName()):result;
return result;
}
});
ts.add(new Student("zhangsan",19));
ts.add(new Student("lisi",15));
ts.add(new Student("wangwu",21));
ts.add(new Student("zhaoliu",19));
ts.forEach(student -> System.out.println(student));
}
}
二叉树
二叉树只能有一个父节点,最多可以有两个子节点。普通的二叉树中数据混乱
二叉查找树
概述:由二叉树升级过来,数据在树中按顺序排列
(1)特点:对于每个节点来说,左子节点的数据小于自己,右子节点的数据大于自己
(2)好处:方便查找,查找的时候一次就能舍弃一边的数据
(3)缺点:添加数据的时候可能出现长短腿的问题,也就是左右子树的深度差距很大
平衡二叉树
概述:也就是树中所有节点的左右子树高度差都不超过1
(1)左旋:当右子树的节点过多的情况下,需要向左旋转。
(2)右旋:当左子树的节点过多的情况下,需要进行向右旋转。
(3)常见旋转
a) 左子树的左子树添加节点(左左)
直接整体右旋
b) 右子树的右子树添加节点(右右)
直接整体左旋
c) 左子树的右子树添加节点(左右)
先左子树部分左旋,然后整体右旋
例子:
在 7 节点的左子树(2,4,5) 的右子树 (5) 添加节点 6
部分左旋,整体右旋
部分左旋,整体右旋。
(1) 部分左旋: 5节点,变成左子树的根节点。4节点 降级成为 左子树 的 左子树。
(2) 整体右旋: 5节点,变成根节点。7节点 降级成为右子树。 6节点 交给7节点的左子树。
d) 右子树的左子树添加节点(右左)
先右子树部分右旋,然后整体左旋
例子:
在7节点的右子树(9,10,11)的右子树(9) 添加节点 8
部分右旋,整体左旋
部分右旋,整体左旋。
(1) 部分右旋: 9节点,变成右子树的根节点。10节点 降级成为 右子树 的 右子树。
(2) 整体左旋: 9节点, 变成根节点。7节点降级成为左子树。 8节点 交给7节点的右子树。