菜鸟之路Day09一一集合进阶(二)

简介: 《菜鸟之路Day09——集合进阶(二)》由blue撰写于2025年1月27日。本文总结了Java集合框架的高级用法,重点介绍了泛型、Set系列集合等内容。泛型特性自JDK5引入,允许在编译阶段约束数据类型,避免运行时异常,并通过泛型类、方法和接口的应用增强了代码灵活性。Set系列集合包括HashSet、LinkedHashSet和TreeSet,分别实现了无序、有序及可排序的元素存储,支持多种遍历方式如迭代器、增强for循环和Lambda表达式。此外,文章详细解析了TreeSet的自然排序与比较器排序机制,提供了丰富的代码示例帮助理解。

菜鸟之路Day09一一集合进阶(二)

作者:blue

时间:2025.1.27

[TOC]

0.概述

内容学习至黑马程序员BV17F411T7Ao,无论如何,今天是值得铭记的一天,我们终于完结了200集的上部。下部漫漫,我们再接再厉。

1.泛型

1.1泛型概述

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

泛型的格式:<数据类型>

注意:泛型只能支持引用数据类型(原因:数据在装入集合的过程中,会做一个泛型的擦除,也就是说在集合中实际存储的是Object类型的数据,而引用数据类型是继承Object的,这里利用了多态,所以这里必须使用引用数据类型。指定了泛型的具体类型后,传入数据时,可以传入该类类型或其子类类型)

泛型的好处:1.统一了数据类型;2.把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能够确定下来

如果不写泛型,类型默认是Object

1.2泛型类

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

比如下面我们手写一个MyArrayList

import java.util.Arrays;

public class MyArrayList<E> {
   
    Object[] obj = new Object[10];
    int size;

    public boolean add(E e){
   
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index) {
   return (E)obj[index];}

    @Override
    public String toString() {
   
        return Arrays.toString(obj);
    }
}

使用一下我们自己写的MyArrayList

public class GenericsDemo1 {
   
    public static void main(String[] args) {
   
        MyArrayList<Integer> list = new MyArrayList<>();
        list.add(123);
        list.add(234);
        list.add(345);
        list.add(456);

        System.out.println(list.get(3));
    }
}

1.3泛型方法

方法中形参类型不确定时:方案①:使用类名后面定义的泛型(所有方法都能用);方案②:在方法申明上定义自己的泛型(只有本方法能用)

泛型方法的格式:

/*
修饰符 <类型> 返回值类型 方法名(类型 变量名){
    方法体
}
*/

练习:定义一个工具类:ListUtil

​ 类中定义一个静态方法addAll,用来添加多个集合的元素

package Generics;

import java.util.ArrayList;

public class ListUtil {
   
    private ListUtil(){
   }//工具类,私有化其构造方法

    //静态方法的话,泛型写在static之后
    public static<E> void addAll(ArrayList<E> list,E e1,E e2,E e3,E e4){
   
        list.add(e1);
        list.add(e2);
        list.add(e3);
        list.add(e4);
    }
}

1.4泛型接口

/*修饰符 interface 接口名<类型>{

}*/

泛型接口的重点在于:如何使用一个带泛型的接口

方式1:实现类给出具体的类型

public class MyArrayList implements List<String>

方式2:实现类延续泛型,创建对象时再确定

public class MyArrayList<E> implement List<E>
MyArrayList<String> list = new MyArrayList<>();

1.5泛型通配符

/*
    ?也表示不确定类型
    他可以进行类型的限定
    ? extends E:表示可以传递E或者E所有的子类类型
    ? super E:表示可以传递E或者E所有父亲类型

    应用场景:
        1.如果我们在定义类,方法,接口的时候,如果类型不确定,就可以定义泛型类,泛型方法,泛型接口
        2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型的通配符
    泛型的通配符:
        关键点:可以限定类型的范围
*/
package Generics;

import java.util.ArrayList;

public class GenericsDemo2 {
   
    public static void main(String[] args) {
   
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();

        method(list1);
        method(list2);
        method(list3);
        //method(new ArrayList<Integer>());这个就不行
    }
    public static void method(ArrayList<? extends Ye> list){
   

    }
}
class Ye {
   

}
class Fu extends Ye{
   

}
class Zi extends Fu {
   

}

2.Set系列集合

Set系列集合:添加的元素是无序,不重复,无索引的

Set集合的实现类特点

名字 特点
HashSet 无序,不重复,无索引
LinkedHashSet 有序,不重复,无索引
TreeSet 可排序,不重复,无索引

2.1遍历方式

Set还是属于Collection系列的所以它的API与Collection是一样,不同是其遍历方式,因为它是无索引的,所以只能用Collection通用的遍历方式,迭代器,增强for,Lambada表达式

package SetDemo;

import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.function.Consumer;

public class SetDemo1 {
   
    public static void main(String[] args) {
   
        HashSet<String> set = new HashSet<>();
        set.add("aaa");
        set.add("aaa");//利用set去重
        set.add("bbb");
        set.add("ccc");
        set.add("ddd");

        //迭代器遍历
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
   
            System.out.println(it.next());
        }

        //增强for
        for(String i:set){
   
            System.out.println(i);
        }

        //Lambda表达式
        set.forEach((String s)->{
   
            System.out.println(s);
        });
    }
}

2.2HashSet

HashSet集合的底层原理采取哈希表存取数据

哈希表是一种对于增删改查数据性能都较好的结构

哈希值

根据hashCode方法算出来的int类型的整数整数

该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算

一般情况下,会重写hashCode方法,利用对象内部属性值计算哈希值

注意:

​ 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的

​ 如果重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的

​ 在小部分情况下,不同属性值或者不同地址值计算出来的哈希值也有可能一样。(哈希碰撞)

练习:

需求

package SetDemo;

import java.util.HashSet;

public class HashSetDemo1 {
   
    public static void main(String[] args) {
   
        HashSet<Student> set = new HashSet<>();
        Student stu1 = new Student("zhangsan",11);
        Student stu2 = new Student("lisi",12);
        Student stu3 = new Student("wangwu",13);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);

        //这个添加是失败的,因为他是根据hashCode来去重的,我们重写了hashCode方法,使得属性一样的hashCode是一致的
        Student stu4 = new Student("zhangsan",11);
        System.out.println(set.add(stu4));//false
        System.out.println(stu1.hashCode());
        System.out.println(stu4.hashCode());

        for(Student i : set){
   
            System.out.println(i);
        }
    }
}

2.3LinkedHashSet

有序,不重复,无索引

这里的有序指的是保证存储和取出的元素顺序是一致的,因为有序的要求更复杂,所以它的效率比HashSet的效率低一点

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

打印出来的顺序是,按照我们添加的顺序

public class LinkedHashSetDemo1 {
   
    public static void main(String[] args) {
   
        LinkedHashSet<Student> set = new LinkedHashSet<>();
        Student stu1 = new Student("zhangsan",11);
        Student stu2 = new Student("lisi",12);
        Student stu3 = new Student("wangwu",13);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);

        System.out.println(set);
    }
}

2.4TreeSet

TreeSet,不重复,无索引,可排序

可排序:按照元素的默认规则(有小到大)排序

TreeSet集合的底层是基于红黑树的数据结构实现排序的,增删改查的性能都比较好

//基本使用
package SetDemo;

import java.util.Iterator;
import java.util.TreeSet;
import java.util.function.Consumer;

public class TreeSetDemo1 {
   
    public static void main(String[] args) {
   
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(2);
        ts.add(1);
        ts.add(5);
        ts.add(4);
        ts.add(3);

        System.out.println(ts);//自动排序

        //增强for
        for(Integer i : ts){
   
            System.out.print(i+" ");
        }
        System.out.println();
        //迭代器
        Iterator<Integer> it = ts.iterator();
        while(it.hasNext()){
   
            System.out.print(it.next()+" ");
        }
        System.out.println();
        //Lambada
        ts.forEach((Integer i)->{
   
            System.out.print(i+" ");
        });

    }
}

TreeSet集合默认的规则

对于数值类型:Integer,Double,默认按照从小到大的顺序排序

对于字符,字符串类型:按照字符在ASCII码表中的数字升序进行排序

自定义类型如何排序

方式一:自然排序/默认排序:javabean类实现Comparable接口指定比较规则

package SetDemo;

import java.util.Objects;

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 o) {
   //按照年龄大小排序
        return this.getAge()-o.getAge();
    }
}
package SetDemo;

import java.util.TreeSet;

public class TreeSetDemo2 {
   
    public static void main(String[] args) {
   
        Student stu1 = new Student("zhangsan",11);
        Student stu2 = new Student("lisi",12);
        Student stu3 = new Student("wangwu",13);

        TreeSet<Student> ts = new TreeSet<>();
        ts.add(stu1);
        ts.add(stu2);
        ts.add(stu3);

        System.out.println(ts);
    }
}

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

package SetDemo;

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

public class TreeSetDemo3 {
   
    public static void main(String[] args) {
   
        //按照长度排序,如果一样长则按照首字母排序
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
   
            @Override
            /*
                o1:表示当前要添加的元素
                o2:表示已经在红黑树中的元素
                返回值的规则和之前是一样的
            */
            public int compare(String o1, String o2) {
   
                //按照长度排序
                int i = o1.length()-o2.length();
                //如果一样长则按照首字母排序
                i=i==0?o1.compareTo(o2):i;//String的内置比较方法
                return i;
            }
        });
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");
    }
}

改为Lambada

package SetDemo;

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

public class TreeSetDemo3 {
   
    public static void main(String[] args) {
   
        //按照长度排序,如果一样长则按照首字母排序
        TreeSet<String> ts = new TreeSet<>(
            (String o1, String o2) ->{
   
                //按照长度排序
                int i = o1.length()-o2.length();
                //如果一样长则按照首字母排序
                i=i==0?o1.compareTo(o2):i;//String的内置比较方法
                return i;
            }
        );
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");
        for (String i:ts){
   
            System.out.println(i);
        }
    }
}
目录
相关文章
|
10月前
|
机器学习/深度学习 算法 数据可视化
利用SVM(支持向量机)分类算法对鸢尾花数据集进行分类
本文介绍了如何使用支持向量机(SVM)算法对鸢尾花数据集进行分类。作者通过Python的sklearn库加载数据,并利用pandas、matplotlib等工具进行数据分析和可视化。
933 70
|
10月前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
325 18
|
10月前
|
机器学习/深度学习 人工智能 运维
智能日志分析:用AI点亮运维的未来
智能日志分析:用AI点亮运维的未来
3234 15
|
10月前
|
机器学习/深度学习 搜索推荐 PyTorch
基于昇腾用PyTorch实现传统CTR模型WideDeep网络
本文介绍了如何在昇腾平台上使用PyTorch实现经典的WideDeep网络模型,以处理推荐系统中的点击率(CTR)预测问题。
506 66
|
10月前
|
传感器 机器学习/深度学习 编解码
NEON LiDAR 数据导出的地表数字模型 (DSM) 和地形数字模型 (DTM)1m分辨率
NEON LiDAR 数据导出的地表数字模型 (DSM) 和地形数字模型 (DTM),分辨率为1米。DSM 包含地表特征(植被和建筑物),DTM 则为裸地高程信息。数据覆盖2013年至今,适用于生态与地形研究。提供 DTM 和 DSM 两个波段,值范围为0-3500米,单位为米。数据遵循 CC0 1.0 协议,无版权限制,鼓励广泛使用。
265 66
|
10月前
|
监控 关系型数据库 MySQL
|
8月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
348 23
|
10月前
|
机器学习/深度学习 算法 PyTorch
昇腾910-PyTorch 实现 ResNet50图像分类
本实验基于PyTorch,在昇腾平台上使用ResNet50对CIFAR10数据集进行图像分类训练。内容涵盖ResNet50的网络架构、残差模块分析及训练代码详解。通过端到端的实战讲解,帮助读者理解如何在深度学习中应用ResNet50模型,并实现高效的图像分类任务。实验包括数据预处理、模型搭建、训练与测试等环节,旨在提升模型的准确率和训练效率。
489 54
|
10月前
|
机器学习/深度学习 数据可视化 数据处理
构建可靠的时间序列预测模型:数据泄露检测、前瞻性偏差消除与因果关系验证
在时间序列分析中,数据泄露、前瞻性偏差和因果关系违反是三大常见且严重影响模型有效性的技术挑战。数据泄露指预测模型错误使用了未来信息,导致训练时表现优异但实际性能差;前瞻性偏差则是因获取未来数据而产生的系统性误差;因果关系违反则可能导致虚假相关性和误导性结论。通过严格的时序数据分割、特征工程规范化及因果分析方法(如格兰杰因果检验),可以有效防范这些问题,确保模型的可靠性和实用性。示例分析展示了日本天然气价格数据中的具体影响及防范措施。 [深入阅读](https://avoid.overfit.cn/post/122b36fdb8cb402f95cc5b6f2a22f105)
552 24
构建可靠的时间序列预测模型:数据泄露检测、前瞻性偏差消除与因果关系验证
|
10月前
|
机器学习/深度学习 人工智能 自然语言处理
Baichuan-Omni-1.5:百川智能开源全模态理解与生成模型,支持文本、图像、音频和视频的多模态输入和输出
Baichuan-Omni-1.5 是百川智能开源的全模态理解模型,支持文本、图像、音频和视频的多模态输入和输出,显著提升多模态交互体验。
762 22
Baichuan-Omni-1.5:百川智能开源全模态理解与生成模型,支持文本、图像、音频和视频的多模态输入和输出