java学习--day18(TreeSet底层&内部类)

2023-09-21 16:30:27


昨天总结:

ArrayList:就是单纯的add
LinkedList: 也是单纯的add
HashSet: 不单纯 得重写equals 和hashCode 方法
TreeSet:不单纯  得在类中去实现Comparable这个接口  让类具有比较排序功能
开发中使用ArrayList

资料:

数据结构学习:http://data.biancheng.net/view/192.html

java8官方文档:https://www.matools.com/api/java8

1.二叉树的了解

通过查阅API我们得知TreeSet集合是基于TreeMap的实现,而TreeMap是基于二叉树(红黑树)结构,也就是说TreeSet集合的底层使用的二叉树(红黑树)结构。

树结构:它也是数据结构中的一种。在计算机领域中树结构指的是倒立的树。

树结构存储的数据,每个数据也需要节点来保存。

而TreeSet集合底层是二叉树的数据结构,什么是二叉树呢?

二叉树:每个节点的下面最多只能有2个子节点。

说明:最多表示一个节点下面可以有两个子节点或者一个子节点或者没有子节点。

在二叉树的根节点左侧的节点称为左子树,在根节点的右侧的节点称为右子树。

既然已经得知TreeSet集合底层是二叉树,那么二叉树是怎样存储数据的呢?是怎样保证存储的数据唯一并有序的呢?

二叉树的存储流程:

当存储一个元素的时候,如果是树的第一个元素,这个元素就作为根节点。

如果不是第一个元素,那么就拿要存储的元素与根节点进行比较大小:

大于根元素:就将要存储的元素放到根节点的右侧,作为右叶子节点。

等于根元素:丢弃。

小于根元素:就将要存储的元素放到根节点的左侧,作为左叶子节点。

总结:二叉树是通过比较大小来保证元素唯一和排序的。
20  10  31  5  13  23 51

案例:

​ 使用TreeSet存储Employee对象,比较两个属性

​ int age, int weight 先按照年龄进行升序排,如果年龄相等的话,按照体重升序排

package com.qfedu.a_treeset;

import java.util.Set;
import java.util.TreeSet;

class Employee implements Comparable<Employee>{
    String name;
    int age;
    int weight;

    public Employee(String name, int age, int weight) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", weight=" + weight +
                '}';
    }

    @Override
    public int compareTo(Employee o) {
        //先按照年两比,如果年龄相等 就比较体重
        int num = this.age - o.age;
        if (num == 0) {
            int num1 = o.weight - this.weight;
            return num1;
        }
        return num;
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Set<Employee> set = new TreeSet<>();
        set.add(new Employee("广坤", 35, 78));
        set.add(new Employee("二贝", 26, 70));
        set.add(new Employee("赵四", 35, 72));
        set.add(new Employee("彩云", 35, 79));
        set.add(new Employee("鸡哥", 32, 59));
        set.add(new Employee("正经博", 32, 59));
        System.out.println(set);
    }
}

案例:

​ TreeSet里面存的是Dog类,

​ 两个属性: String name, int age

​ 先按照字符串的字典顺序排,然后字符串相等的话,在按照年龄排

字符串也有compareTo方法,按字典顺序比较两个字符串。 比较是基于字符串中每个字符的Unicode值

package com.qfedu.a_treeset;

import java.util.Set;
import java.util.TreeSet;

class Dog implements Comparable<Dog>{
    String name;
    int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Dog o) {
        //先按照字典的顺序进行排,如果字符串相等再按照年龄升序排
        int num = this.name.compareTo(o.name);
        if (num == 0) {
            //字符串相等的情况,又要比较年领
            int num1 = this.age - o.age;
            return num1;

        }
        return num;
    }
}
public class Demo3 {
    public static void main(String[] args) {
        Set<Dog> set = new TreeSet<>();
        set.add(new Dog("彩云", 5));
        set.add(new Dog("旺财", 2));
        set.add(new Dog("大黄", 6));
        set.add(new Dog("大黄", 3));
        set.add(new Dog("大黄", 4));
        System.out.println(set);
    }
}

总结:使用TreeSet的时候需要类实现一个接口 Comparable这个接口去做比较排序

但是就只有这一种方式进行排序吗?不是的!!!还有一种比较器的写法Comparator这个接口

2.使用比较器将数据存储到TreeSet中

package com.qfedu.b_comparator;

import java.util.Comparator;
import java.util.Set;
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 +
                '}';
    }
}
class MyComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        int num = o1.age - o2.age;
        return num;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //如果想要使用比较器的写法 必须再new TreeSet的时候
        //加上比较器对象
        //TreeSet 有一个有参构造,有参构造的方法是Comparwator的对象
        //Comparator是一个接口 不能实例化,咋办?再写一个类去实现这个接口
        Set<Student> students = new TreeSet<>(new MyComparator());
        students.add(new Student("维一", 23));
        students.add(new Student("永康", 19));
        students.add(new Student("赵娜", 18));
        students.add(new Student("运铎", 28));
        students.add(new Student("佳祥", 36));
        System.out.println(students);
    }
}

3.匿名内部类

为了减少代码量

3.1基于抽象类的匿名内部类
package com.qfedu.c_anno;

//声明一个抽象列类
abstract class Person {
    public abstract void eat();
    public void sleep() {
        System.out.println("好想逃,然后去睡觉!!!");
    }
}
//常规来讲,新建一个类 去继承抽象类。然后实例化继承的抽象类的类
//class Man extends Person {
//
//    @Override
//    public void eat() {
//        System.out.println("好饿,想吃肉!!!");
//    }
//}
public class Demo1 {
    public static void main(String[] args) {
        //匿名内部类:在实例化对抽象类同时并重写抽象方法
        Person person = new Person(){
            @Override
            public void eat() {
                System.out.println("吃吃喝喝");
            }
        };
        person.eat();
        person.sleep();

    }
}

package com.qfedu.c_anno;

abstract class Animal{
    public abstract void call();
    public abstract void call1();
    
}
public class Demo2 {
    public static void main(String[] args) {
        new Animal(){
            @Override
            public void call() {
                System.out.println("哇哇叫!!!");
            }

            @Override
            public void call1() {
                System.out.println("哈哈叫");
            }
        }.call();
        new Animal(){
            @Override
            public void call() {
                System.out.println("哇哇叫!!!");
            }

            @Override
            public void call1() {
                System.out.println("哈哈叫");
            }
        }.call1();
    }
}

3.2基于接口的匿名内部类
package com.qfedu.c_anno;

interface A {
    void testA();

}
public class Demo3 {
    public static void main(String[] args) {
//        A a = new A(){
//
//            @Override
//            public void testA() {
//                System.out.println("嘻嘻哒");
//            }
//        };
//        a.testA();
        new A(){

            @Override
            public void testA() {
                System.out.println("哈哈");
            }
        }.testA();
    }
}

package com.qfedu.c_anno;

import java.util.Comparator;
import java.util.TreeSet;
import java.util.Set;

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 +
                '}';
    }
}
public class Demo4 {
    //存到TreeSet里面
    public static void main(String[] args) {
        Set<Student> set = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
               int num =  o1.age - o2.age;
                return num;
            }
        });
        set.add(new Student("维一", 23));
        set.add(new Student("永康", 19));
        set.add(new Student("赵娜", 18));
        set.add(new Student("运铎", 28));
        set.add(new Student("佳祥", 36));
        System.out.println(set);
    }
}

真实开发的时候,一个方法参数是一个接口对象,不用再新建一个类去实现这个接口,直接方法中去new 接口

package com.qfedu.c_anno;

interface B {
    void eat();
}
public class Demo5 {
    public static void main(String[] args) {
        test(new B() {
            @Override
            public void eat() {
                System.out.println("吃锦旗");
            }
        });
    }
    public static void test (B b) {
        b.eat();
    }
}

4.内部类

从字面意思来理解:在类的内部创建一个类,这个类叫内部类

了解即可,开发禁止使用(效率太低)

4.1成员内部类

在类的方法的外面再类中,就是成员内部类

package com.qfedu.d_inner;

class MemberDemo {
    String name = "张三";
    int age = 20;

    public void printf() {
        System.out.println("打印着玩");
    }
    class Inner {//就是内部类
        String name = "李四";
        //在Inner这个类中可以访问外部类的属性和方法
        public void test () {
            printf();//打印着玩
            System.out.println(age);//20
            System.out.println(name);//李四
            //如果访问外部特定的属性的时候: 类名.this.属性
            System.out.println(MemberDemo.this.name);//张三
        }
    }

}
public class Demo1 {
    public static void main(String[] args) {
        //成员内部类的创建步骤:
        //1.实例化外部类 类对象
        MemberDemo memberDemo = new MemberDemo();
        //2.实例化内部类对象,但是new得改为  外部对象.new
        MemberDemo.Inner inner = memberDemo.new Inner();
        inner.test();

    }
}

更多推荐

【二分法查找】

使用二分法查找需要注意的点:使用二分法的前提:数组为有序数组,同时题目还强调数组中无重复元素。二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。写二分法,区间的定义一般为两种

clickhouse简单安装部署

目录前言(来源于官方文档):一.下载并上传1.下载地址:点我跳转下载2.上传至Linux二.解压和配置1.解压顺序注意:必须按照以下顺序解压,并且每解压一个都要执行该解压后文件的install/doinst.sh文件解压步骤:三.启动1.启动服务2.启动客户端四.建表语法与clickhouse数据类型1.建表语法2.数

UI设计师面试时如何自我介绍优漫动游

学会了UI设计技术,接下来的计划就是要找工作了,UI设计在面试环节的自我介绍很重要,有时候一分钟的自我介绍已经足够让HR判断出你适不适合他们公司,那做为一名UI设计师面试时如何自我介绍呢?来看看下面的详细介绍。UI设计师面试时如何自我介绍UI设计师面试时如何自我介绍:怎么做好一次自我介绍?1、自我介绍内容:结合面试岗位

什么是高阶成分(HOC)?解释 React 中 render() 的目的?

高阶成分(HOC)是一种基于React的组合特性而形成的设计模式。HOC是自定义组件,在其中包裹了另一个组件。他们可以接受任何动态提供的子组件,但不会修改或复制其输入组件中的任何行为。您可以说HOC是"纯"组件1。HOC通过对组件逻辑的重用,让组件逻辑可复用、可测试、可维护,从而提高开发效率1。在React中,rend

利用爬虫技术自动化采集汽车之家的车型参数数据

导语汽车之家是一个专业的汽车网站,提供了丰富的汽车信息,包括车型参数、图片、视频、评测、报价等。如果我们想要获取这些信息,我们可以通过浏览器手动访问网站,或者利用爬虫技术自动化采集数据。本文将介绍如何使用Python编写一个简单的爬虫程序,实现对汽车之家的车型参数数据的自动化采集,并使用亿牛云爬虫代理服务来提高爬虫的稳

Java AOP Framework概述

JavaAOPFramework概述1.AspectJ1.1使用AspectJ进行切面编程2.SpringAOP2.1使用SpringAOP进行切面编程2.2如何决定使用哪种动态代理2.3如何通过配置指定代理方式2.4SpringAOP和AspectJ的关系3.SpringBootAOP4.扩展4.1AspectJ织入

面向面试知识--MySQL数据库与索引

面向面试知识–MySQL数据库与索引优化难点与面试点什么是MySQL索引?索引的MySQL官方定义:索引是帮助MySQL快速获取数据的数据结构。动力节点原文:MysQL官方对于索引的定义:索引是帮助MySQL高效获取数据的数据结构。MysQL在存储数据之外,数据库系统中还维护着满足特定查找算法的数据结构,这些数据结构以

HarmonyOS Codelab 优秀样例——溪村小镇(ArkTS)

一、介绍溪村小镇是一款展示溪流背坡村园区风貌的应用,包括园区内的导航功能,小火车行车状态查看,以及各区域的风景展览介绍,主要用于展示HarmonyOS的ArkUI能力和动画效果。具体包括如下功能:打开应用时进入启动页,启动页轮播展示溪村小镇风景图,之后进入应用首页。在首页的“地图浏览”标签页,可以拖动和缩放查看地图,并

Kubernetes Ingress:灵活的集群外部网络访问的利器

《KubernetesIngress:集群外部访问的利器-打造灵活的集群网络》前提条件您已经拥有一个Kubernetes集群,并且可以访问该集群。您已经安装了kubectl命令行工具。版本选择安装前需要选择兼容你Kubernetes的版本,不能会失败ingress由两部分组成:IngressController:负责处

【踩坑日记】springboot MultipartFile上传,@Async异步执行时报错:java,io.FileNotFoundException

项目场景:springboot项目中使用MultipartFile上传文件导入时,文件内容过大会导致页面等待时间较长,所以考虑使用上传文件时用@Async异步处理数据的方式来解决页面等待问题。问题描述给处理MultipartFile文件的方法添加@Async注解后,上传文件时出现异常,找不到临时文件异常如下:(org.

RISC-V Reader 笔记(七)RV64,特权架构,未来可选扩展

RV64比起RV32,其实扩展不多。主要是添加了一系列字,双字为单位的操作。各个ISA3264比较x86:变量都存在寄存器里,不像32存在内存里,因此指令数少很多,但是因此添加了很多新操作码来操作更多的寄存器,因此指令长度变长了(添加前缀来区分),代码体积大很多。arm:有一系列和arm32类似的问题,:分支指令使用的

热文推荐