Java基础总结(一)

news2024/11/6 7:33:02

文章目录

  • 前言
  • 封装
  • 继承
  • 多态
  • 抽象方法
  • 接口
  • 内部类
  • static
  • 权限修饰符
  • this super
  • private关键字
  • final关键字
  • 就近原则
  • 构造方法
  • ==号
  • StringBuilder
  • StringJoiner
  • 字符串原理总结:
    • 1、字符串存储的内存原理
    • 2、==号比较的是什么?
    • 3、字符串拼接的底层原理
    • 4、StringBuilder提高效率的原理
  • StringBuilder源码分析
  • Arrays
    • Lambda表达式
  • 集合
    • 集合体系结构
    • Collection【单列集合】
      • Collection的遍历方式
      • 增强for遍历
      • Lambda表达式遍历
    • List集合
      • List的遍历方式
    • List实现类
      • ArrayList常用方法【有序可重复】
      • ArrayList集合底层原理
      • LinkedList常用方法【有序可重复】
    • 泛型
    • 红黑树
    • Set集合
      • HashSet集合
      • LinkedHashSet集合
      • TreeSet集合
    • Map集合【双列集合】
      • map集合的遍历方式
      • HashMap集合
      • LinkedHashMap集合
      • TreeMap集合


前言


封装

封装:正确设计对象的属性和方法
原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为
封装思想的好处

  • 让编程变得很简单,有什么事,找对象,调方法就行
  • 降低学习成本,有需要时去找就行

继承

继承【自己设计+用别人的】:Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系
优点

  • 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
  • 子类可以在父类的基础上,增加其他的功能,使子类更强大

什么时候用继承? 当类和类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码

特点

  • Java只支持单继承:一个子类只能有一个父类
  • Java不支持多继承,但支持多层继承【子类A继承父类B,父类B可以继承父类C,C是A的间接父类 -->祖孙三代】
  • Java中每一个类都直接或者间接的继承于Object类
  • 子类只能访问父类中非私有的成员

子类能继承父类中的哪些内容
父类的构造方法不能被继承
父类中的成员变量可以被继承【非私有 private都可以 只是private修饰的成员变量不能用而已】
只有父类中的虚方法才能被子类继承
【虚方法:非private 非static 非final】

继承中成员变量的访问特点:
就近原则。先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
在这里插入图片描述
继承中成员方法的访问特点:
同成员变量,就近原则

继承中构造方法的访问特点:
父类中的构造方法不会被子类继承,但是可以通过super调用
子类中所有的构造方法默认先访问父类中的无参构造,再执行自己

为什么
子类在初始化之前,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
子类初始化之前,一定要调用父类构造方法先完成父类数据空间初始化
怎么调用父类构造方法
子类构造方法的第一行默认语句都是:super()。不写也存在。且必须在第一行

方法重写
当父类的方法不能满足子类现在的需求时,需要进行方法重写

方法重写注意事项和要求
1、重写方法的名称、形参列表必须与父类中保持一致
2、子类重写父类方法时,访问权限子类必须大于等于父类(缺省<protected<public)
3、子类重写父类方法时,返回值类型必须小于等于父类
4、只有被添加到虚方法表中的方法才能被重写

多态

多态:同类型的对象,表现出的不同形态
多态的前提

  • 有继承/实现关系
  • 父类型引用指向子类型对象
  • 有方法重写

多态的好处
使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利

调用成员变量:编译看左边,运行也看左边
调用成员方法:编译看左边,运行看右边

多态的优势

  • 在多态形势下,右边对象可以实现解耦合,便于扩展和维护
  • 定义方法的时候,使用父类型作为参数,可以接受所有子类对象,体现多态的扩展性与遍历

多态的弊端
不能调用子类的特有功能

原因
当调用成员方法的时候,编译看左边,运行看右边,在编译的时候会先检查左边类中的有没有这个方法,如果没有直接报错
解决方案
变回子类型即可
有可能会发生转换类型与真实对象类型不一致的问题,因此在转换的时候可以使用关键字instanceof关键字判断

抽象方法

抽象方法:将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体,该方法就可以定义为抽象方法
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象方法和抽象类的注意事项

  • 抽象类不能实例化
  • 抽象类中不一定有抽象方法,由抽象方法的类一定是抽象类
  • 可以有构造方法

不能创建对象有什么用呢?当创建子类对象时,给属性进行赋值

  • 抽象类的子类要么重写抽象类中的所有抽象方法,要么是抽象类

抽象方法和抽象类的意义
强制子类必须按照这种格式进行重写

接口

接口:就是一种规则【侧重于行为】,是对行为的抽象

接口类的注意事项

  • 接口和类的实现关系,可以单实现,也可以多实现
  • 实现类还可以在继承一个类的同时实现多个接口

接口中成员的特点
成员变量:只能是常量 默认修饰符public static final
构造方法:没有
成员方法:在JDK7之前 接口中只能定义抽象方法默认修饰符public abstract 【只要接口中发生变化,所有的实现类都要发生变化】

【又想加新的方法,又不想报错】
JDK8以后接口中新增的方法

允许在接口中定义默认方法,需要使用关键字default修饰
允许在接口中定义静态方法,需要使用关键字static修饰
作用:解决接口升级问题
接口中默认方法的注意事项
1、默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
2、public可以省略,default不能省略
3、如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
接口中静态方法的注意事项
静态方法不需要重写

JDK9以后接口中新增的方法

接口中可以定义私有方法【普通的私有方法、静态的私有方法】,为了解决JDK8中允许定义的默认和静态方法有一些重复的代码需要提取出来,而提取出来的代码又不想被外界直接调用,定义私有方法

接口和类之间的关系
类和类的关系

继承关系,只能单继承,不能多继承,但是可以多层继承

类和接口的关系

实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

接口和接口的关系

继承关系,可以单继承,也可以多继承

内部类

内部类:下载一个类里面的类就叫做内部类
什么时候用到内部类?B类表示的事物是A类的一部分,且B单独存在没有意义,汽车的发动机,ArrayList的迭代器
分类:成员内部类、静态内部类、局部内部类、匿名内部类

在这里插入图片描述
成员内部类
在这里插入图片描述
静态内部类
在这里插入图片描述
在这里插入图片描述
局部内部类
在这里插入图片描述
匿名内部类
匿名内部类:隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式的细节:包含了继承或实现,方法重写,创建对象
整体就是一个类的子类对象或者接口的实现类对象
使用场景:当方法的参数是接口或者类时,以接口为例,可以传递这个接口的是西安类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码

在成员内部类中,JDK16之前不能定义静态变量,JDK16之后才可以定义静态变量

static

静态变量

(在堆内存当中会开辟一个静态存储位置【静态区】 对象共享的,在内存当中只有一份)
static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量

特点: 被该类所有对象共享;不属于对象属于类;静态变量是随着类的加载而加载,优先于对象出现的
调用方式:类名调用;对象名调用

静态方法

特点:多用在测试类和工具类中;JavaBean类中很少会用
调用方式:类名调用;对象名调用

static注意事项

静态方法只能访问静态变量和静态方法
非静态方法可以访问所有
静态方法中没有this关键字

权限修饰符

权限修饰符:是用来控制一个成员能都被访问的范围的
在这里插入图片描述

this super

this:理解为一个变量,表示当前方法调用者的地址值
super:代表父类存储空间

在这里插入图片描述

private关键字

1、权限修饰符
2、可以修饰成员(成员变量或者方法)
3、被private修饰的成员只能在本类中才能访问
4、针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作
5、提供setXxx(参数)方法,用于给成员变量赋值,方法用public修饰
6、提供getXxx(参数)方法,用于获取成员变量的值,方法用public修饰

final关键字

final修饰方法:表明该方法是最终方法,不能被重写
final修饰类:表示该类是最终类,不能被继承
final修饰变量:叫做常量,不能被修改
细节

  • final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
  • final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部可以改变

就近原则

就近原则:谁离我更近,我就用谁
第一个age会先在局部位置中去找,如果能找到,那么它使用的就是局部位置的age
第二个age直接使用成员遍历位置的age
在这里插入图片描述
this的作用:区分成员变量和局部变量
this的本质:代表方法调用者的地址值

构造方法

构造方法也叫做构造器、构造函数
作用:在创建对象时,由虚拟机自动调用,给成员变量进行初始化
特点:
1、方法名要和类名完全一致
2、没有返回值类型,void也没有
3、没有具体的返回值(不能由return带回结果数据)
构造方法的定义:
1、如果没有写任何的构造方法,那么虚拟机会给我们加一个无参构造方法
2、如果定义了构造方法,系统将不再提供默认的构造方法

==号

==号比较的到底是什么?
1、基本数据类型:比较的是具体的数据值
在这里插入图片描述

2、引用数据类型:比较的是地址值
在这里插入图片描述
那么字符串比较应该用什么呢?
boolean equals 完全一样结果才是true,否则为false;
boolean equalsIgnoreCase 忽略大小写比较【验证码输入】
键盘输入的字符串实际上是new出来的

StringBuilder

StringBuilder可以看成是一个容器,创建之后里面的内容是可变的
在这里插入图片描述

StringJoiner

StringJoinerStringBuilder 一样,也可以看成是一个容器,创建之后里面的内容是可变的。
作用:提高字符串的操作效率,而且代码编写特别简洁,但目前市场上很少有人使用
JDK8之后才出现
在这里插入图片描述
在这里插入图片描述

字符串原理总结:

1、字符串存储的内存原理

直接复制会复用字符串常量池中的
new出来不会复用,而是开辟一个新的空间

2、==号比较的是什么?

基本数据类型比较数据值
引用数据类型比较地址值

3、字符串拼接的底层原理

等号右边没有变量
String s1 = "abc"
拼接的时候没有变量,都是字符串。触发字符串的优化机制,在编译的时候就已经是最终的结果了,会复用串池中的字符串
等号右边有变量
String s1 = "abc";
String s2 = "d";
String s3 = "ab"+s2;
JDK8以前:系统底层会自动创建一个StringBuilder对象,然后再调用其append方法完成拼接。拼接后,再调用其toString方法转换为String类型,而toString方法的底层是直接new了一个字符串对象。
一个加号,堆内存中俩对象【StringBuilder String】,浪费时间,浪费性能
JDK8以后:系统会预估字符串拼接之后的总大小,把要拼接的内容都放在数组中,此时也是产生一个新的字符串。

4、StringBuilder提高效率的原理

把所有的东西都往StringBuilder这个容器中去放,不会创建很多无用的空间,节约内存
疑问?会撑爆吗?
StringBuilder默认容量是16
默认创建一个长度为16的字节数组
添加的内容长度小于16,直接存
添加的内容大于16会扩容:2*老容量+2 = 34
如果超出扩容的容量,则以实际长度为准

StringBuilder源码分析

默认容量
在这里插入图片描述
append方法
在这里插入图片描述
进入这个方法 :先会进行一次非空判断,如果不是null
1、先获取到现在添加的字符的长度
2、去判断是否需要扩容
在这里插入图片描述
进入这个方法
拿着3去减老容量16,小于0不需要扩容
假设需要扩容 str = “a-z” 26个英文字母
此时26-10>0,需要扩容
在这里插入图片描述

进入这个方法

在这里插入图片描述
进入这个方法

16+18 也就相当于 2*老容量+2 = 34 完成扩容
在这里插入图片描述

Arrays

操作数组的工具类
在这里插入图片描述

binarySearch:二分法查找元素

细节一:二分法查找的前提:数组中的元素必须是有序的,数组中的元素必须是升序的
细节二:如果要查找的元素是存在的,那么返回的是真实的索引,否则返回的是-(插入点-1)

copyOf:拷贝数组

细节:如果新数组的长度是小于老数组的长度,会部分拷贝
细节:如果新数组的长度是大于老数组的长度,会补上默认初始值

copyOfRange:拷贝数组(指定范围)

细节:包头不包尾,包左不包右

sort:按照指定的规则排序
第二个参数是一个接口,所以我们在调用方法的时候,需要传递这个接口的实现类对象,作为排序规则,采用匿名内部类的方式

底层原理:利用插入排序+二分查找的方式进行排序的。默认把0索引的数据当作是有序的序列,1索引到最后认为是无序的序列。遍历无须的序列里得到的每一个元素,假设当前遍历得到的元素是A元素,把A往有序序列中插入,在插入的时候,是利用二分查找确定A元素的插入点。
拿着A元素和插入点的元素进行比较,比较的规则就是compare方法的方法体,如果方法的返回值是负数,拿着A继续跟前面的数据进行比较;如果方法的返回值是正数,拿着A继续和后面的元素比较;如果方法的返回值是0,也拿着A元素和后面的元素进行比较,直到能确定A的最终位置为止
compare方法的形式参数:
参数一 o1:表示在无序序列中,遍历得到的每一个元素
参数二 o2:表示有序序列中的元素
返回值:
负数:表示当前要插入的元素是小的,放在前面
正数:表示当前要插入的元素是大的,放在后面
0:表示当前要插入的元素跟现在的元素比是一样的,也会放在后面
结论:
o2- o1:降序排列
o1- o2:升序排列

Lambda表达式

函数式编程是一种思想特点
函数式编程思想,忽略面向对象的复杂语法,强调做什么,而不是谁去做

作用:
简化函数式接口的匿名内部类的写法
注意点:
Lambda表达式可以用来简化匿名内部类的书写
Lambda表达式只能简化函数式接口的匿名内部类写法
函数式接口:有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加@FunctionalInterface注解
省略规则:
1、参数类型可以省略不写
2、如果只有一个参数,参数类型可以省略 同时()也可以省略
3、如果Lambda表达式的方法体只有一行,大括号,分号,return可以省略不写,需要同时省略

    public static void main(String[] args) throws ParseException {
        Integer[] arr = {2,1,4,5,3,7,8,9};

        //匿名内部类的完整格式
        Arrays.sort(arr,new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });

        //lambda表达式的完整格式
        Arrays.sort(arr,(Integer o1, Integer o2) ->{
                return o1-o2;
            }
        );

        //lambda表达式的省略写法
        Arrays.sort(arr,(o1, o2) ->o2-o1);
        System.out.println(Arrays.toString(arr));
    }

例题:
定义数组并存储一些字符串,利用Arrays中的sort方法进行排序
要求:按照字符串的长度进行排序,短的在前面,长的在后面

    public static void main(String[] args) throws ParseException {
        String[] arr2 = {"a","aaaa","aaa","aa"};
        //匿名内部类的完整格式
        Arrays.sort(arr2, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        });
         //lambda表达式的完整格式
        Arrays.sort(arr2, (String o1, String o2)-> {
                return o1.length()-o2.length();
            }
        );
        //lambda表达式的省略写法
        Arrays.sort(arr2, (o1, o2)-> o1.length()-o2.length());
        
        System.out.println(Arrays.toString(arr2));
    }

集合

集合和数组的对比:
1、长度:数组长度固定;集合长度可变
2、存储类型:数组可以存基本数据类型,也可以存引用数据类型;集合可以存引用数据类型【基本数据类型需要转换为包装类】

集合体系结构

在这里插入图片描述

  • List系列集合:有序可重复,有索引 (存和取的顺序是一样的)
  • Set系列集合:无序不可重复,无索引 (存和取的顺序是不一样的)【可以用来进行数据去重】

Collection【单列集合】

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的

Collection的遍历方式

  • 迭代器遍历
    迭代器在Java中的类是Iterator,是集合专用的遍历方式

在这里插入图片描述

    public static void main(String[] args) throws ParseException {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");

        //获取迭代器对象
        Iterator it = coll.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

细节:

  1. 迭代器遍历完毕指针不会复位。如果要继续第二次遍历集合,只能再次获取一个新的迭代器对象
  2. 循环中只能用一次next方法 【next方法获取元素并且移动指针】
  3. 迭代器遍历时,不能用集合的方法进行删除或增加。【会报ConcurrentModificationException 错误
    并发修改异常】,如果非要删除 可以使用迭代器提供的remove方式去删除,如果要添加,暂时没有办法
  4. 迭代器的指针已经指向了最后没有元素的位置时会报NoSuchElementException
    ,为什么不是索引越界,因为迭代器在遍历集合的时候是不依赖于索引的

增强for遍历

增强for的底层就是迭代器,是在JDK5之后出现的,所有的单列集合和数组才能用增强for进行遍历

    public static void main(String[] args) throws ParseException {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");

        for (String c:coll){
            System.out.println(c);
        }
    }

细节:

  1. 修改增强for中的变量,不会改变集合中原本的数据

Lambda表达式遍历

JDK8之后

先采用匿名内部类的方式

    public static void main(String[] args) throws ParseException {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");

        //2、利用匿名内部类的方式实现
        //底层原理
        coll.forEach(new Consumer<String>() {
            @Override
            //s依次表示集合中的每一个数据
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }

foreach底层原理
自己也会遍历集合,依次得到每一个元素,把得到的每一个元素,传递给下面的accept方法
在这里插入图片描述

采用Lambda方式

    public static void main(String[] args) throws ParseException {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");

        //3、利用lambda方式实现
        coll.forEach( s-> System.out.println(s));
    }

List集合

Collection的方法List都继承了,List因为有所有,所以多了很多索引操作的方法
在这里插入图片描述
List集合中两个删除的方法:
1、直接删除元素
2、通过索引进行删除
在这里插入图片描述

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//此时删除的是索引下标为1的元素 而不是数据为1的元素
list.remove(1);

因为在调用方法的时候,如果出现了方法重载,会优先调用实参和形参类型一致的方法
如果想删除数据元素1

list.remove(0);
//或者
//需要进行手动装箱
Integer i = Integer.valueOf(1);
list.remove(i);

List的遍历方式

  • 迭代器遍历
        Iterator it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
  • 增强for遍历
    (变量s,其实就是一个第三方的变量而已,在循环过程中,依次遍历集合中的每个元素)
        for (String s : list) {
            System.out.println(s);
        }
  • lambda表达式遍历
		list.forEach(s -> System.out.println(s));
  • 普通for循环(有索引)
        for(int i=0;i< list.size();i++){
            System.out.println(list.get(i));
        }
  • 列表迭代器遍历
    ListIterator
        //额外添加了一个方法,在遍历的过程中可以通过迭代器添加元素
        ListIterator<String> lt = list.listIterator();
        while(lt.hasNext()){
            System.out.println(lt.next());
        }

遍历方式总结:

  • 迭代器遍历:在遍历过程中需要删除元素,需要使用迭代器
  • 列表迭代器遍历:在遍历过程中需要添加元素,使用列表迭代器
  • 增强for遍历以及lambda表达式遍历:仅仅想遍历,不进行任何操作,可使用这两个
  • 普通for:如果遍历的时候想操作索引

List实现类

ArrayList常用方法【有序可重复】

在这里插入图片描述

ArrayList集合底层原理

ArrayList底层是数组结构

利用空参创建的集合,在底层创建一个默认长度为0的数组
添加第一个元素时,底层会创建一个新的长度为10的数组 elementData
当数组添加满之后,会自动扩容为1.5倍
如果一次添加多个元素,1.5倍还放不下,则新创建的数组长度以实际为准

空参构造:
在这里插入图片描述
在这里插入图片描述
添加元素:

数组长度加一,把现有个数+1
在这里插入图片描述
当超出数组范围,就会进行自动扩容
在这里插入图片描述

在集合中不仅仅是一次添加一个元素,还可以一次添加多个元素
第一种情况:如果一次添加一个元素,那么进行数组扩容,并将原数组拷贝到扩容后的新数组中并返回即可
第二种情况:如果一次添加多个元素,假设100,此时数组需要扩容到100个单位才行
在这里插入图片描述
在这里插入图片描述
第三种情况:超出范围
在这里插入图片描述

LinkedList常用方法【有序可重复】

底层数据结构是链表
在这里插入图片描述

底层原理:
在这里插入图片描述
尾插法:
在这里插入图片描述
在这里插入图片描述

头插法:
在这里插入图片描述

迭代器的底层源码:

这个内部类就表示是ArrayList的迭代器,所以当我们调用多次这个方法的时候,那么就相当于是创建了多个迭代器的对象
在这里插入图片描述

在这里插入图片描述

拿着集合现在变化的次数,和创建对象时传递过来的次数进行比较
在这里插入图片描述

如何避免并发修改异常

在使用迭代器或者是增强for遍历集合的过程中,不要使用集合的方法去添加或者删除元素即可

泛型

JDK5引入的特性,可以在编译阶段约束操作的数据类型,并进行检查

没有泛型的时候,集合如何存储数据?

如果我们没有给集合指定类型,默认为所有的数据类型都是Object类型,此时可以给集合添加任意的数据类型,带来一个坏处:我们在获取数据的时候,无法使用它的特有行为【多态的弊端:不能访问子类的特有功能】

此时推出了泛型,泛型的好处:

  • 统一数据类型
  • 把运行时期的问题提前拿到了编译时期,避免了强制类型转换可能出现的异常,因为在编译阶段就能确定下来

Java中的泛型是伪泛型
在这里插入图片描述

泛型的细节:

  • 泛型中不能写基本数据类型【基本数据类型是没有办法转化为Object类型,只能转换为包装类】
  • 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型【数据是有继承性的】
  • 如果不写泛型,类型默认是Object

方法中形参类型不确定时:

  • 使用类名后面定义的泛型【所有方法都能用】
  • 在方法声明上定义自己的泛型【只有本方法能用】

泛型接口的两种使用方式:

  • 实现类给出具体的类型
  • 实现类延续泛型,创建实现类对象时再确定类型

泛型的继承

  • 泛型不具备继承类型,但是数据具备继承性【泛型里面写的是什么类型,那么只能传递什么类型的数据】
    在这里插入图片描述
    在这里插入图片描述

利用泛型方法,有一个弊端:此时它可以接受任意的数据类型:Ye Fu Zi Student……
在这里插入图片描述

在这里插入图片描述

那么这个方法虽然不确定类型,但是希望以后只能传递 Ye Fu Zi
此时我们就可以使用泛型的通配符:

?表示不确定的类型 可以进行类型限定
? extends E:表示可以传递E或者E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
在这里插入图片描述
Student 跟这个继承结构没有关系,因此就会报错
在这里插入图片描述

应用场景:

  • 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
  • 如果类型不确定,但是能直到以后只能传递某个继承体系中的,就可以使用泛型的通配符【限定泛型的类型】

红黑树

红黑树是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色
每一个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过红黑规则进行实现的

在这里插入图片描述
为什么要发明红黑树?
平衡二叉树AVL:插入/删除很容易破坏“平衡”特性,需要频繁调整数的形态。例如插入操作导致不平衡,需要先计算平衡因子,找到最小不平衡树(时间开销较大),再进行左旋右旋调整。

红黑树RBT:插入/删除很多时候不会破坏“红黑”特性,无需频繁调整树的形态。即便需要调整,一般都可以在常数级时间内完成

红黑规则

  • 每一个节点或是红色,或者是黑色的
  • 根节点必须是黑色
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点是黑色的
  • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红节点相连的情况)
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

红黑添加节点的规则

  • 添加节点默认是红色的(效率高)

在这里插入图片描述

Set集合

无序:存和取的顺序不一致
不重复:可以用来元素去重
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Collection中的三种遍历方式都适用【迭代器、增强for、Lambda表达式】

Set实现类的特点

  • HashSet:无序、不可重复、无索引
  • LinkedHashSet:有序、不可重复、无索引
  • TreeSet:可排序、不可重复、无索引

HashSet集合

HashSet集合底层采用哈希表存取数据
哈希表是一种对于增删改查数据性能都比较好的结构

哈希表组成
JDK8之前:数组+链表
JDK8之后:数组+链表+红黑树

hashCode方法:
根据hashCode方法计算出int类型的整数
这个方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算【意义不大】
一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

如果没有重写hashCode方法,不同对象计算出的哈希值是不同的【地址值计算出来的】
如果已经重写hashCode方法,不同对象只要属性值相同,计算出的哈希值是一样的【属性值计算出来的】
在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样【哈希碰撞】

HashSet 底层原理:

在这里插入图片描述

JDK8以后,当链表长度超过8,而且数组长度大于等于64时,会自动转换为红黑树
集合中存储的是自定义对象时,必须要重写hashCode和equals方法

LinkedHashSet集合

有序、不重复、无索引
原理:底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序

如果使用数据去重使用哪个?

默认使用HashSet
如果要求去重且存取有序,才使用LinkedHashSet

TreeSet集合

不可重复、无索引、可排序
可排序:按照元素的默认规则(由小到大)排序
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好

TreeSet的两种比较方式

1. 默认排序/自然排序:JavaBean类实现Comparable接口指定比较规则

实现接口,并重写抽象方法

this:表示当前要添加的元素
o:表示已经在红黑树中的元素

    @Override
    public int compareTo(Student o) {
        //指定排序的规则:
        //只看年龄 想要按照年龄升序进行排列
        return this.getAge()-o.getAge();
    }
    public static void main(String[] args) throws ParseException {
        TreeSet set = new TreeSet();
        Student s1 = new Student("zhangsan",22);
        Student s2 = new Student("lisi",18);
        Student s3 = new Student("wangwu",30);
        Student s4 = new Student("zhaoliu",10);
        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        System.out.println(set);
    }

在这里插入图片描述

2. 比较器排序:创建TreeSet对象的时候,传递比较器Comparator指定规则

例题:按照长度排序,如果一样长则按照首字母排序

    public static void main(String[] args) throws ParseException {
        TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length()==0?o1.charAt(0)-o2.charAt(0) : o1.length()-o2.length();
            }
        });
        set.add("cdfdgbfnbcv");
        set.add("ab");
        set.add("ef");
        set.add("qwer");
        System.out.println(set);
    }

在这里插入图片描述

Map集合【双列集合】

双列集合的特点:

  • 双列集合一次需要存一对数据,分别为键和值
  • 键不能重复,值能重复
  • 键和值是一一对应的
  • 键+值 在Java中叫做Entry对象

Map是双列集合的顶层接口,全部的双列集合都可以继承使用
在这里插入图片描述
put方法的细节:

1、在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合中
2、如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回

map集合的遍历方式

1、键找值

       //把map的key放到一个set集合当中
        Set<String> keys = map.keySet();
        //遍历集合
        for(String key:keys){
            System.out.println(key);
            System.out.println(map.get(key));
        }

2、键值对

       //把map的键值对放到一个set集合当中
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for(Map.Entry<String, String> entry:entries){
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }

3、Lambda表达式

匿名内部类

        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key);
                System.out.println(value);
            }
        });

Lambda表达式

		map.forEach((key, value)-> System.out.println(key+value));

foreach方法的底层:
在这里插入图片描述

foreach其实就是利用键值对方式进行遍历,依次得到每一个键和值

HashMap集合

无序、不重复、无索引

transient Node<K,V>[] table; 哈希表结构中数组的名字
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 数组默认长度16
static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子0.75

HashMap里面每一个对象包含以下内容:

链表中的键值对对象:
在这里插入图片描述

红黑树中的键值对对象:
除了键的哈希值、键、值等还有以下内容:
在这里插入图片描述
添加元素

添加元素的时候至少考虑三种情况:
2.1数组位置为null
2.2数组位置不为null,键重复,元素覆盖
2.3数组位置不为null,键不重复,元素挂在下面形成链表或者红黑树

在这里插入图片描述

在这里插入图片描述

Node<K,V>[] tab; 定义一个局部变量,用来记录哈希表中数组的地址值
Node<K,V> p; 临时的第三方变量,用来记录键值对对象的地址值
int n; 表示当前数组的长度
int i; 表示索引

在这里插入图片描述

1、如果是第一次添加数据,底层会创建一个默认长度为16,加载因子为0.75的数组
2、如果不是第一次添加数据,会看数组中元素是否达到了扩容条件
2.1如果没有达到扩容条件,底层不会做任何操作
2.2达到了扩容条件,底层会把数组扩容为原先的两倍,并把数据全部转移到新的哈希表中
3、把当前数组的长度赋值给n

添加第一个元素详细过程–数组位置为null

在这里插入图片描述
i = (n-1)&hash 拿着数组的长度跟键的哈希值进行计算,计算出当前键值对对象,在数组中应存入的位置
p=tab[i]; 获取数组中对应元素的数组
if p==null 底层会创建一个键值对对象,直接放到数组当中
中间略去了else那段代码
在这里插入图片描述
threshold 记录的就是数组的长度*0.75,表示哈希表的扩容时机
return null; 表示当前没有覆盖任何元素,返回null

添加第一个元素详细过程—数组位置不为null

走上边略掉的那段else代码
p.hash == hash 数组中键值对的哈希值 当前要添加键值对的哈希值
在这里插入图片描述
给链表中挂节点:
在这里插入图片描述

大的else还没结束
【发生覆盖:】
如果e!=null :表示当前的键是一样的,值会被覆盖
在这里插入图片描述else结束

LinkedHashMap集合

有序、不重复、无索引

TreeMap集合

可排序、不重复、无索引
TreeMap和TreeSet底层原理一样,都是红黑树结构的
可排序:对键进行排序

TreeMap中每一个节点的内部属性
在这里插入图片描述
TreeMap中空参构造
没有传递比较器对象
在这里插入图片描述

TreeMap中带参构造
传递了比较器对象
在这里插入图片描述

TreeMap添加第一个元素详细过程–root为null

在这里插入图片描述

TreeMap添加第二个元素详细过程–root不为null

不会进入根节点为null的判断,直接继续往后执行


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428869.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASIC-WORLD Verilog(1)一日Verilog

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。 这是网站原文&…

Java反射面试总结(二)

为什么引入反射概念&#xff1f;反射机制的应用有哪些&#xff1f; 我们来看一下 Oracle 官方文档中对反射的描述&#xff1a; 从 Oracle 官方文档中可以看出&#xff0c;反射主要应用在以下几方面&#xff1a; 反射让开发人员可以通过外部类的全路径名创建对象&#xff0c;…

详解C语言结构体内存对齐:你知道如何快速计算结构体大小吗?

本篇博客会讲解C语言结构体的内存对齐&#xff0c;并且给出一种快速计算结构体大小的方式。主要讲解下面几点&#xff1a; 结构体的内存对齐是什么&#xff1f;如何快速计算结构体的大小&#xff1f;如何利用内存对齐节省结构体占用的内存空间&#xff1f;为什么结构体要内存对…

分布式数据库架构路线大揭秘

文章目录分布式数据库是如何演进的&#xff1f;数据库与分布式中间件有什么区别&#xff1f;如何处理分布式事务&#xff0c;提供外部一致性&#xff1f;如何处理分布式SQL&#xff1f;如何实现分布式一致性&#xff1f;数据库更适合金融政企的未来这些年大家都在谈分布式数据库…

MySQL-中间件mycat(一)

目录 &#x1f341;mycat基础概念 &#x1f341;Mycat安装部署 &#x1f343;初始环境 &#x1f343;测试环境 &#x1f343;下载安装 &#x1f343;修改配置文件 &#x1f343;启动mycat &#x1f343;测试连接 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f9…

边缘网关thingsboard-gateway DTU902

thingsboard-gateway是一个采用python语言编写的开放源代码网关程序&#xff0c;用于将传统或第三方系统的设备与thingsboard平台连接。 支持 采集Modbus slaves、CAN、MQTT 、OPC-UA servers, Sigfox Backend。 除了具备普通 网关外&#xff0c;还具备可配置的边缘能力&…

rabbitmq深入实践

生产者&#xff0c;交换机&#xff0c;队列&#xff0c;消费者 交换机和队列通过 rounting key 绑定者&#xff0c;rounting key 可以是#.,*.这类topic模式&#xff0c; 生产者发送消息内容 rountingkey&#xff0c; 到达交换机后交换机检查与之绑定的队列&#xff0c; 如果能匹…

Yolov5之common.py文件解读

深度学习训练营原文链接前言0.导入需要的包以及基本配置1.基本组件1.1 autopad1.2 ConvDWConv模块1.3TransformerLayer模块1.4 Bottleneck和BottleneckCSPBottleneck模型结构1.5 CrossConv模块1.6 C3模块基于C3的改进1.7SPP1.8Focus模块1.9 Concat模块1.10 Contract和Expand1.1…

好东西!!!多亏几位大牛整理的面试题,让我成功上岸!!

凡事预则立&#xff0c;不预则废。相信很多程序员朋友在跳槽前都会临阵磨枪&#xff0c;在网络上搜集一些面试题进行准备。 然而&#xff0c;当机会来临时&#xff0c;却发现这些面试题往往“不快也不光”.... 由于Java面试涉及的范围很广&#xff0c;很杂&#xff0c;而且技…

使用MyBatis实现简单查询

文章目录一&#xff0c;创建数据库与表&#xff08;一&#xff09;在Navicat里创建MySQL数据库testdb&#xff08;二&#xff09;创建用户表 - t_user&#xff08;三&#xff09;在用户表里插入3条记录二&#xff0c;案例演示MyBatis基本使用&#xff08;一&#xff09;创建Mav…

解决idea每次打开新的项目都需要重新配置maven

原理&#xff1a;就是通过 idea 来进行全局配置【非当前工程配置】 IDEA 版本&#xff1a;2023.1 如何查看版本信息 &#xff1f; 【主菜单】——【帮助】——【关于】 我在网上查找了许多文章 &#xff0c;我混淆了一点&#xff01;当前工程的设置 & 全局设置 不在一个地方…

马斯克掷重金收购英

人前主义&#xff0c;人后生意。在带领一众科技圈大佬签署了呼吁暂停研发比GPT-4更强AI模型的公开信后不久&#xff0c;马斯克却转头豪掷千金收购了10000块英伟达GPU。 一些网友吐槽&#xff0c;以马老板的格局而言&#xff0c;这次价值过亿的投资绝对不是为了借着AI概念火爆来…

2021年 团体程序设计天梯赛——题解集

Hello各位童学大家好&#xff01;&#x1f60a;&#x1f60a;&#xff0c;茫茫题海你我相遇即是缘分呐&#xff0c;或许日复一日的刷题已经让你感到疲惫甚至厌倦了&#xff0c;但是我们真的真的已经达到了我们自身极限了吗&#xff1f;少一点自我感动&#xff0c;没有结果前别太…

[FREERTOS] 任务的创建、删除、调度与状态

1.什么是任务&#xff1f; 我的理解是&#xff1a;任务像是进程/线程&#xff0c;创建一个任务就会开辟一个空间&#xff0c;每一个任务都是独立的执行相应的动作互不干扰&#xff0c;就比如玩游戏&#xff0c;陪女朋友&#xff0c;任务通常都会有一个while(1)死循环 2.与任务创…

使用cloudflare代理flask启用https服务

原文来自&#xff1a;使用cloudflare代理flask启用https服务 | 夜空中最亮的星 欢迎大家留言讨论 问题1&#xff1a;使用cloudflare的dns回源服务器的时候&#xff0c;出现了http和https不断反复重定向 问题2: flask只能启用http服务&#xff0c;需要启用https 步骤 服务器&…

浅谈[Linux搭建GitLab私有仓库,并内网穿透实现公网访问]

转载自远控源码文章&#xff1a;Linux搭建GitLab私有仓库&#xff0c;并内网穿透实现公网访问 前言 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的Web服务。 Gitlab是被广泛使用的基于git的开源代码管理平…

报错解决:Python ‘NoneType‘ object is not subscriptable , 获取到的数据为None,需要保留数据

人生苦短&#xff0c;我用python 爬取某DB电影数据的时候&#xff0c; 在获取内容的时候出现 NoneType object is not subscriptablePython 资料报错交流:点击此处跳转文末名片获取 获取数据的部分代码是&#xff1a; writer_avatars (writers_list[wi][avatars][small]) …

Linux0.11 信号(十二)

系列文章目录 Linux 0.11启动过程分析&#xff08;一&#xff09; Linux 0.11 fork 函数&#xff08;二&#xff09; Linux0.11 缺页处理&#xff08;三&#xff09; Linux0.11 根文件系统挂载&#xff08;四&#xff09; Linux0.11 文件打开open函数&#xff08;五&#xff09…

前端webpack项目性能优化——体积压缩和compression-webpack-plugin的使用

前端webpack项目性能优化——体积压缩和compression-webpack-plugin的使用需求优化结果需求 脚手架搭建的项目&#xff0c;会默认开启sourceMap&#xff0c;此时打包下来的包会很大&#xff0c;如图&#xff1a;map文件比所有js文件都大&#xff0c;会导致包整体体积过大&…

NIFI大数据进阶_Json内容转换为Hive支持的文本格式_操作方法说明_01_EvaluteJsonPath处理器---大数据之Nifi工作笔记0031

然后我们再来看一下如何把json内容,转换成hive支持的文本格式,其实还是一个格式转换对吧 首先看一下用到的处理器,可以看到这里我们用到了evaluateJsonPath处理器,这个处理器用来提取json中的熟悉,然后ReplaceText处理器用来替换掉FlowFile中的属性的内容 首先看一下这个Evalua…