1.TreeSet
1.1TreeSet的底层是二叉树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HaGG2wwX-1659617918949)(C:\Users\张海涛\Desktop\1.png)]
1.2二叉树的定义
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分 [1] 。
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点
二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树
1.3TreeSet的泛型可以是对象,但由于在存入时是自然排序,需要实现Comparable接口,自定义的对象本身是没有实现这个方法的,所以需要实现这个方法并重写。
注意:return的返回值是对自己选择条件的判断的差值,是int类型的数据,但重写默认值为0,所以判断条件需要自己写。
package com.qfedu.a_test;
import java.util.TreeSet;
class Toy implements Comparable<Toy> {
int age;
int weight;
public Toy(int age, int weight) {
this.age = age;
this.weight = weight;
}
@Override
public String toString() {
return "Toy{" +
"age=" + age +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Toy o) {
//根据weight和age进行正序排列
int num1 = this.weight - o.weight;
if (this.weight == o.weight) {//排除weight相等的特殊情况
int num = this.age - o.age;
return num;
}
return num1;//返回值是一个int类型的数据
}
}
public class Demo2 {
public static void main(String[] args) {
Toy toy = new Toy(18, 60);
Toy toy1 = new Toy(16, 60);
Toy toy2 = new Toy(28, 70);
Toy toy3 = new Toy(38, 68);
Toy toy4 = new Toy(10, 36);
TreeSet<Toy> set = new TreeSet<>();
//存入数据时系统会进行自然排序,但需要实现Comparable这个接口
set.add(toy);
set.add(toy1);
set.add(toy2);
set.add(toy3);
set.add(toy4);
System.out.println(set);
}
}
通过查阅API我们得知TreeSet集合是基于TreeMap的实现,而TreeMap是基于二叉树(红黑树)结构,也就是说TreeSet集合的底层使用的二叉树(红黑树)结构。
树结构:它也是数据结构中的一种。在计算机领域中树结构指的是倒立的树。
树结构存储的数据,每个数据也需要节点来保存。
而TreeSet集合底层是二叉树的数据结构,什么是二叉树呢?
二叉树:每个节点的下面最多只能有2个子节点。
说明:最多表示一个节点下面可以有两个子节点或者一个子节点或者没有子节点。
在二叉树的根节点左侧的节点称为左子树,在根节点的右侧的节点称为右子树。
既然已经得知TreeSet集合底层是二叉树,那么二叉树是怎样存储数据的呢?是怎样保证存储的数据唯一并有序的呢?
二叉树的存储流程:
当存储一个元素的时候,如果是树的第一个元素,这个元素就作为根节点。
如果不是第一个元素,那么就拿要存储的元素与根节点进行比较大小:
大于根元素:就将要存储的元素放到根节点的右侧,作为右叶子节点。
等于根元素:丢弃。
小于根元素:就将要存储的元素放到根节点的左侧,作为左叶子节点。
总结:二叉树是通过比较大小来保证元素唯一和排序的。
20 10 31 5 13 23 51
2.比较器
原理:通过构建一个类来实现Comparator这个接口,由于这个这个接口只有一个抽象方法,所以只需要实现一个,需要注意的是,这个Comparator要增加泛型限制,并且是Student这个限制,再重新这个抽象方法时,需要根据自己选择的判断方式来写,返回值为int类型的数据,判断标准为大于小于等于三种情况。
package com.qfedu.a_test;
import java.util.Comparator;
import java.util.TreeSet;
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//构造一个比较器,实现Comparator中的抽象方法
//比较器是TreeSet独有的方法
class MyComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
//根据年龄进行排序
int num = o1.age - o2.age;//正序
//int num = o1.age - o2.age;//逆序
return num;
}
}
public class Demo3 {
public static void main(String[] args) {
Student student = new Student("狗蛋", 24);
Student student1 = new Student("小王", 36);
Student student2 = new Student("小刘", 32);
Student student3 = new Student("彩云", 16);
//比较器中是自己排序的根据,在这个地方是age
//比较器作为一个参数引入到TreeSet中
TreeSet<Student> tree = new TreeSet<>(new MyComparator());//引用比较器
tree.add(student);
tree.add(student1);
tree.add(student2);
tree.add(student3);
System.out.println(tree);
}
}
注意:比较器是TreeSet独有的方法
3.匿名内部类
3.1基于抽象类的匿名内部类
package com.qfedu.b_test;
abstract class Person{
public abstract void eat();
}
public class Demo1 {
public static void main(String[] args) {
//在new的时候重写抽象类中的方法
Person person = new Person(){
@Override
public void eat() {
System.out.println("吃东西");
}
};
//调用此方法
person.eat();
}
}
3.2基于接口的匿名内部类
package com.qfedu.b_test;
interface People{
void eat();
}
public class Demo2 {
public static void main(String[] args) {
//在接口后进行重写,省去了新建一个对象来继承
new People(){
@Override
public void eat() {
System.out.println("吃大餐");
}
}.eat();//在此处可以直接调用对象重写后的方法
}
}
意义:省去了创造一个类去实现一个接口
4.内部类
4.1要访问内部类,可以通过创建外部类的对象,然后创建内部类的对象来实现。
嵌套类有两种类型:
非静态内部类
静态内部类
4.2非静态内部类
非静态内部类是一个类中嵌套着另外一个类。 它有访问外部类成员的权限, 通常被称为内部类。由于内部类嵌套在外部类中,因此必须首先实例化外部类,然后创建内部类的对象来实现。
4.3私有的内部类
内部类可以使用 private 或 protected 来修饰,如果你不希望内部类被外部类访问可以使用 private 修饰符:
4.4 静态内部类
静态内部类可以使用 static 关键字定义,静态内部类我们不需要创建外部类来访问,可以直接访问它:
4.5从内部类访问外部类成员
内部类一个高级的用法就是可以访问外部类的属性和方法:
package com.qfedu.b_test;
class Dog{
String name = "狗蛋";
int age = 12;
// public Dog(String name, int age) {
// this.name = name;
// this.age = age;
public void play1(){
System.out.println("就是玩");
}
//这个类就是内类
class Cat{
int age = 10;
public void play(){
//可调用外部类的方法
play1();
//这是调用自身的属性
System.out.println(age);
//可以调用外部类的属性
System.out.println(Dog.this.name);//狗当前的属性
}
}
}
public class Demo3 {
public static void main(String[] args) {
// Dog dog = new Dog("大黄" , 2);
Dog dog = new Dog();
//可构建Cat类的对象
Dog.Cat cat = dog.new Cat();
//通过对象调用方法
cat.play();
//可输出自身的属性
System.out.println(cat.age);
}
}
void main(String[] args) {
// Dog dog = new Dog(“大黄” , 2);
Dog dog = new Dog();
//可构建Cat类的对象
Dog.Cat cat = dog.new Cat();
//通过对象调用方法
cat.play();
//可输出自身的属性
System.out.println(cat.age);
}
}