JavaSE进阶第八天
课程大纲:
- 泛型
- Set集合
- TreeSet
- HashSet
- 数据结构-二叉树
一.泛型
1.什么是泛型
广泛的类型,类似于小数数学中的x变量,你想让它是什么类型就可以是什么类型
泛型是JDK5之后出现的新特性(自动拆装箱、增强for和泛型),
泛型是一种在编译时期的一种安全机制,泛型只在编译时期有效,编译之后没有了(泛型的擦除)
2.泛型的好处
- 把运行时期的问题提前到了编译时期
- 避免了强制类型转换
3.泛型可以使用的地方
4.如何使用泛型
泛型一般定义在<>中,要起一个名称(变量名),名称可以是一个字母组成,也可以是多个字母,
一个泛型可以是一部分组成,也可以多多部分组成。泛型在传递的时候,只能传递引用类型
<E>
<T>
<K>
<V>
<HELLO>
<AB>
<K,V>
<X,Y,Z,...>
5.泛型的格式
5.1泛型类
public class 类名<E> {
}
E泛型变量,在类中肯定就会使用到,可以用在方法上(返回值类型、参数类型、方法体中变量类型)
或者成员变量上,当做类型来使用。
什么时候确定泛型具体类型:创建对象的时候确定泛型具体类型
//就是一个泛型类
public class Box<E> {
private E Element;
public E getElement() {
return Element;
}
public void setElement(E element) {
Element = element;
}
}
Box<String> box = new Box<>();
box.setElement("给小丽的土味情话!");
System.out.println(box.getElement());
Box<Integer> inter = new Box<>();
inter.setElement(11);
System.out.println(inter.getElement());
5.2泛型方法
修饰符 <T> 返回值类型 方法名(参数列表) {
}
T泛型变量,在方法中肯定会使用到,可以用在当前泛型方法上(返回值类型、参数类型、方法体中变量类型),出了该方法无法使用。
什么时候确定泛型具体类型:调用的时候确定泛型具体类型
ArrayList<String> list = new ArrayList<>();
list.add("给小丽的土味情话");
list.add("给小路的土味情话");
list.add("给小花的土味情话");
// 将list集合转成一个数组并返回
// 如果是空参的,那么返回的数组类型为Object类型的
Object[] objects = list.toArray();
System.out.println(Arrays.toString(objects) +"-----"+"Object类型");
System.out.println("-------------------------");
// 将list集合转成一个数组并返回
// list.toArray(T[] a)
String[] strings = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(strings)+"-----"+"String类型");
public static void main(String[] args) {
ArrayList<String> list = addElement(new ArrayList<String>(), "a", "b", "c", "d");
System.out.println(list+"-------"+"String类型");
System.out.println("------------------------");
ArrayList<Integer> inter = addElement(new ArrayList<Integer>(), 1, 2, 3, 4);
System.out.println(inter+"-------"+"Integer类型");
}
public static <T> ArrayList<T> addElement(ArrayList<T> list,T t1,T t2,T t3,T t4){
list.add(t1);
list.add(t2);
list.add(t3);
list.add(t4);
return list;
}
5.3泛型接口
public interface 接口名<E> {
}
E泛型变量,在接口中肯定就会使用到,可以用在方法上(返回值类型、参数类型)或者常量上,当做类型来使用。
什么时候确定泛型具体类型:
1、创建实现类对象的时候确定泛型具体类型
2、定义实现类的时候确定泛型具体类型
3、定义子接口的时候确定泛型具体类型
interface Genericity<E>{
public abstract void method(E e);
}
class GenericityImpl1<E> implements Genericity<E>{
@Override
public void method(E e) {
System.out.println(e);
}
}
GenericityImpl1<String> genericity = new GenericityImpl1<>();
genericity.method("小丽给我的土味情话");
方式二
class GenericityImpl2 implements Genericity<Integer>{
@Override
public void method(Integer integer) {
System.out.println(integer);
}
}
class GenericityImpl3 implements Genericity<String>{
@Override
public void method(String string) {
System.out.println(string);
}
}
GenericityImpl2 genericityImpl2 = new GenericityImpl2();
genericityImpl2.method(12);
System.out.println("--------------");
GenericityImpl3 objectGenericityImpl3 = new GenericityImpl3();
objectGenericityImpl3.method("给安迪的土味情话");
5.4类型通配符
- 类型通配符:<?>
- ArrayList<?> :表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
- 但是并不能把元素添加到ArrayList中,获取出来的也是父类类型
类型通配符上限:<? extends 类型>
ArrayList<? extends Number>:它表示的类型是Number或者其子类型
类型通配符下限:<? super 类型>
ArrayList<? super Number>:它表示的类型是Number或者其父类型
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Number> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
printList(list1);
printList(list2);
public static void printList(ArrayList<?> list) { //可以传任意泛型
}
// 表示传递进来集合的类型,可以是Number类型,也可以是Number所有的子类类型
public static void method1(ArrayList<? extends Number> list) {
}
// 表示传递进来集合的类型,可以是Number类型,也可以是Number所有的父类类型
public static void method2(ArrayList<? super Number> list) {}
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Number> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
method1(list1); //number的子类
method1(list3); //numberz自己
// method1(list4); // 报错 Object类型比Number要大
// method2(list1); 报错,Integer类型比Number小
method2(list4); //number的父类
method2(list3); //number自己
5.5Collection中涉及泛型通配符的方法
boolean addAll(Collection<? extends E> c):将b集合中的所有元素添加到a集合中
boolean containsAll(Collection<?> c):判断a集合中是否包含b集合中所有的元素
boolean removeAll(Collection<?> c):删除a集合中包含b集合的元素(删除交集)
boolean retainAll(Collection<?> c):保留a集合中包含b集合的元素(保留交集)
要操作这些方法必须有两个集合,一个是调用者集合,一个是参数集合
a.addAll(b),a就是调用者集合,b就是参数集合
二.Set集合
1.Set集合的特点
- 不能存储重复
- 没有索引,不能使用普通for循环遍历,也不能通过索引来获取,删除集合里面的元素
- 不能保证存取有序
Set<String> set = new TreeSet<>();
set.add("ccc");
set.add("aaa");
set.add("aaa");
set.add("bbb");
只能用增强for或迭代器遍历
Iterator<String> 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);
}
2.TreeSet
2.1TreeSet集合特点
- 不包含重复元素的集合
- 没有带索引的方法
- 可以将元素按规律进行排序
2.2 自然排序
2.2.1实现方式:
- 将元素所在的类实现Comparable接口
- 在类中重写抽象方法compareTo方法( compareTo方法中的代码就是用于设置规则的)
2.2.2 compareTo方法
两个对象:
this和o
都是当前类的对象
this:代表当前要添加的元素
o:集合中已经存储的元素
this在前,o在后是升序
o在前,this在后是降序
返回值
正数:元素放在后边(右边/下面)
0:删除
负数:元素放在前边(左边/上面)
TreeSet<Student> ts = new TreeSet<>();
Student s1 = new Student("dahei",80,80,80);
Student s2 = new Student("erhei",90,90,90);
Student s4 = new Student("erhei2",95,85,90);
Student s3 = new Student("xiaohei",100,100,100);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
for (Student t : ts) {
System.out.println(t);
}
public class Student implements Comparable<Student>{}
public int compareTo(Student o) {
int result = o.getScore() - this.getScore();
result = result == 0 ? o.getChinese() - this.getChinese() : result;
result = result == 0 ? o.getMath() - this.getMath() : result;
result = result == 0 ? o.getEnglish() - this.getEnglish() : result;
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}
2.3 比较器排序
2.3.1实现方式
- 定义一个类实现Comparator接口
- 在实现类中重写抽象方法compare方法(compare方法中的代码就是用于设置规则的)
- 在创建TreeSet对象的时候,将实现类对象传递到TreeSet的构造方法中
2.3.2 compare方法
两个对象:
o1和o2
表示元素对象
o1:代表当前要添加的元素
o2:集合中已经存储的元素
o1在前,o2在后是升序
o2在前,o1在后是降序
返回值
正数:元素放在后边(右边/下面)
0:删除
负数:元素放在前边(左边/上面)
注:o1相当于自然排序中的this
o2相当于自然排序中的o
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
@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);
System.out.println(ts);
2.4两种排序方式的选择
- 自定义类: 两种方式任选一种,都能达到一样的目的,一般选择自然方式比较多
- JDK提供的类: 比如Integer、String等,里面有默认的自然方式排序,当默认的方式不满足条件的时候选择比较器排序
- 当两种排序方式同时存在的时候,比较器排序方法会覆盖自然排序方法
三.二叉树
3.1结构
3.1.1节点的结构
3.1.2二叉树的结构
- 高度(层数) :4
- 最顶层的节点:根节点
- 4是7的左节点,10是7的右节点
3.1.3二叉查找树
特点: 1.每一个节点上最多有两个子节点
2.每一个节点的左子节点都是小于自己的
3.每一个节点的右子节点都是大于自己的
3.2二叉查找树添节点
规则:
小的存左边
大的存右边
一样的不存
3.3平衡二叉树
- 二叉树左右两个子树的高度差不超过1
- 任意节点的左右两个子树都是一颗平衡二叉树
3.4二叉查找树
条件:利用左旋和右旋机制保证树的平衡
注意点:
- 判断添加元素与当前节点的关系
- 成功添加之后,判断是否破坏了二叉树的平衡
3.5左旋
- 逆时针旋转
- 右子节点变成父节点(根节点)
- 原先的根节点降级变成左子节点
- 将多余的左子节点出让,给降级的节点作为右子节点
3.6右旋
- 顺时针旋转
- 左子节点变成父节点(根节点)
- 原先的根节点降级变成右子节点
- 将多余的右子节点出让,给降级的节点作为左子节点
3.7旋转的四种情况
3.7.1左左
当根节点左子树的左左子树节点插入,导致二叉树不平衡
3.7.2左右
当根节点左子树的右子树节点插入,导致二叉树不平衡
3.7.3右右
当根节点右子树的右子树有节点插入,导致二叉树不平衡
3.7.4右左
当根节点右子树的左子树有节点插入,导致二叉树不平衡
h9J-1631619754621)]
[外链图片转存中…(img-WxvYmz0z-1631619754621)]
3.7.2左右
当根节点左子树的右子树节点插入,导致二叉树不平衡
[外链图片转存中…(img-g8CAtXxu-1631619754622)]
[外链图片转存中…(img-FCDZyupa-1631619754622)]
[外链图片转存中…(img-UbdzvBbV-1631619754623)]
[外链图片转存中…(img-kNJg1gsz-1631619754623)]
3.7.3右右
当根节点右子树的右子树有节点插入,导致二叉树不平衡
[外链图片转存中…(img-JiEO64nj-1631619754623)]
[外链图片转存中…(img-fgkzj9ak-1631619754624)]
[外链图片转存中…(img-4HiWodV2-1631619754624)]
3.7.4右左
当根节点右子树的左子树有节点插入,导致二叉树不平衡
[外链图片转存中…(img-l6LCd7az-1631619754625)]
[外链图片转存中…(img-UznRa7Vh-1631619754625)]
[外链图片转存中…(img-amp6FGBC-1631619754626)]