【数据结构】泛型

news2024/12/23 18:22:46

⭐ 作者:小胡_不糊涂
🌱 作者主页:小胡_不糊涂的个人主页
📀 收录专栏:浅谈Java
💖 持续更文,关注博主少走弯路,谢谢大家支持 💖

泛型

  • 1. 包装类
    • 1.1 基本数据类型和对应的包装类
    • 1.2 装箱和拆箱
    • 1.3 自动装箱和拆箱
  • 2. 什么是泛型
    • 2.1 语法
  • 3. 泛型类的使用
  • 4. 泛型如何编译
    • 4.1 擦除机制
    • 4.2 为什么不能实例化泛型类型数组
  • 5. 泛型的上界
  • 6. 泛型方法

在这里插入图片描述

1. 包装类

在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了
一个包装类型。

1.1 基本数据类型和对应的包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

除了 Integer 和 Character,其余基本类型的包装类都是首字母大写。

1.2 装箱和拆箱

int i = 10;

// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);

// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();

1.3 自动装箱和拆箱

可以看到在使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者的负担,java 提供了自动机制。

int i = 10;

Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱

int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱

有奖竞答:下列代码输出什么,为什么?

public static void main(String[] args) {
	Integer a = 127;
	Integer b = 127;
	Integer c = 128;
	Integer d = 128;
	System.out.println(a == b);
	System.out.println(c == d);
}

先看一下 Integer 的源码,可以知道被 Integer 修饰的变量取值范围是 -128~127。
在这里插入图片描述
所以,答案应该是:

在这里插入图片描述

2. 什么是泛型

泛型: 就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。

首先解决这样一个问题:
实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值?

按照我们之前的想法是使用数组,但数组只能存放指定类型的元素,例如:int[] array = new int[10]; String[] strs = newString[10];
而且所有类的父类,默认为Object类。数组是否可以创建为Object?

class MyArray {
    public Object[] array = new Object[10];
    public Object getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,Object val) {
        this.array[pos] = val;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setVal(0,10);
        myArray.setVal(1,"hello");//字符串也可以存放
        String ret = myArray.getPos(1);//编译报错
        System.out.println(ret);
    }
}

在这里插入图片描述
上述代码中,虽然在 myArray 中任何类型的数据都可以存放,而且1号下标的元素是String类型的,但仍编译报错,此处必须进行强制类型转换。

虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类型。而不是同时持有这么多类型。

所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

2.1 语法

class 泛型类名称<类型形参列表> {
	// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
	class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
	// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
	// 可以只使用部分类型参数
}

将上述代码进行改写如下:

class MyArray<T> {
	public T[] array = (T[])new Object[10];//注释1
	public T getPos(int pos) {
	return this.array[pos];
	}
	public void setVal(int pos,T val) {
	this.array[pos] = val;
	}
}

public class TestDemo {
	public static void main(String[] args) {
		MyArray<Integer> myArray = new MyArray<>();//注释2
		myArray.setVal(0,10);
		myArray.setVal(1,12);
		int ret = myArray.getPos(1);//注释3
		System.out.println(ret);
		myArray.setVal(2,"hello");//注释4
	}
}

代码解释:

  1. 类名后的 代表占位符,表示当前类是一个泛型类。

类型形参一般使用一个大写字母表示,常用的名称有:

E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型

  1. 上面代码注释1处,不能new泛型类型的数组。

比如下面的语句就是不对的:

T[] ts = new T[5];//error
  1. 注释2处,类型后加入 <Integer> 指定当前类型。
  2. 注释3处,不需要进行强制类型转换。
  3. 注释4处,代码编译报错,此时因为在注释2处指定类当前的类型,此时在注释4处,编译器会在存放元素的时候帮助我们进行类型检查。

3. 泛型类的使用

语法:

泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象

示例:

MyArray<Integer> list = new MyArray<Integer>();

MyArray<Integer> list = new MyArray<>(); // 可以推导出实例化需要的类型实参为Integer

🍩泛型只能接受类,所有的基本数据类型必须使用包装类!

4. 泛型如何编译

4.1 擦除机制

通过cmd窗口的命令:javap -c 查看字节码文件,可见所有的T都是Object。

在这里插入图片描述
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。
Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

4.2 为什么不能实例化泛型类型数组

先看一段代码:

class MyArray<T> {
	public T[] array = (T[])new Object[10];
	public T getPos(int pos) {
		return this.array[pos];
	}
	public void setVal(int pos,T val) {
		this.array[pos] = val;
	}
	public T[] getArray() {
		return array;
	}
}

public static void main(String[] args) {
	MyArray<Integer> myArray1 = new MyArray<>();
	Integer[] strings = myArray1.getArray();
}

/*
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at TestDemo.main(TestDemo.java:31)
*/

报错原因:替换后的方法为:将Object[]分配给Integer[]引用,程序报错。
在getArray()返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的

将上述代码修改为:

class MyArray<T> {
	public T[] array;
	public MyArray() {
	}
	
	/**
	* 通过反射创建,指定类型的数组
	* @param clazz
	* @param capacity
	*/
	public MyArray(Class<T> clazz, int capacity) {
		array = (T[])Array.newInstance(clazz, capacity);
	}
	public T getPos(int pos) {
		return this.array[pos];
	}
	public void setVal(int pos,T val) {
		this.array[pos] = val;
	}
	public T[] getArray() {
		return array;
	}
}

public static void main(String[] args) {
	MyArray<Integer> myArray1 = new MyArray<>(Integer.class,10);
	Integer[] integers = myArray1.getArray();
}

5. 泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
语法:

class 泛型类名称<类型形参 extends 类型边界> {
	...
}

示例:

public class MyArray<E extends Number> {
	...
}

🍩只接受 Number 的子类型作为 E 的类型实参

MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型

🍩没有指定类型边界 E,可以视为 E extends Object

复杂示例:

public class MyArray<E extends Comparable<E>> {
	...
}

🍩E必须是实现了Comparable接口的

6. 泛型方法

语法:

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }

示例1:

public class Util {
	//静态的泛型方法 需要在static后用<>声明泛型类型参数
	public static <E> void swap(E[] array, int i, int j) {
		E t = array[i];
		array[i] = array[j];
		array[j] = t;
	}
}

示例2:可以类型推导

Integer[] a = { ... };
swap(a, 0, 9);

String[] b = { ... };
swap(b, 0, 9);

示例3:不使用类型推导

Integer[] a = { ... };
Util.<Integer>swap(a, 0, 9);

String[] b = { ... };
Util.<String>swap(b, 0, 9);

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

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

相关文章

【Javascript保姆级教程】Javascript数据类型和算术运算符

文章目录 前言一、JavaScript数据类型1.1 数字&#xff08;Number&#xff09;1.2 字符串&#xff08;String&#xff09;1.3 布尔&#xff08;Boolean&#xff09;1.4 数组&#xff08;Array&#xff09;1.5 类(Object) 二、算术运算符2.1 加法&#xff08;&#xff09;2.2 减…

单目3D目标检测——SMOKE 环境搭建|模型训练

本文分享SMOKE最新的版本的环境搭建&#xff0c;以及模型训练&#xff1b;环境关键库版本&#xff1a;pytorch 1.12.0、CUDA 11.3、cudnn 8.3.2、python 3.7、DCNv2。 目录 1、docker 获取Nvidia 镜像 2、安装Conda 3、创建SMOKE环境 4、编译SMOKE环境 5、下载kitti 3D目标…

Springboot+vue的校园资产管理系统(有报告),Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的校园资产管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的校园资产管理系统&#xff0c;采用M&#xff…

让Pegasus天马座开发板实现超声波测距

在完成《让Pegasus天马座开发板用上OLED屏》后&#xff0c;我觉得可以把超声波测距功能也在Pegasus天马座开发板上实现。于是在箱子里找到了&#xff0c;Grove - Ultrasonic Ranger 这一超声波测传感器。 官方地址: https://wiki.seeedstudio.com/Grove-Ultrasonic_Ranger 超声…

使用postcss-pxtorem插件实现px转换rem

1.下载postcss-pxtorem(其他插件按需下载自行配置)并在package.json同级目录下新建postcss.config.js文件: export const defaultHtmlFontSize 37.5 export default {plugins: {autoprefixer: {overrideBrowserslist: [Android > 4.0, iOS > 7],},postcss-pxtorem: {//…

tensorrt获取输入输出

利用Netron打开onnx&#xff0c;右边名字&#xff1a; int input_index engine->getBindingIndex("inout1.1");int output_index engine->getBindingIndex("191");

NSSCTF做题

[第五空间 2021]WebFTP 打开题目 发现是登录的界面 用admin和password试一下发现不行 用dirsearch扫一下 发现了git泄露 但是用githack下载不下来文件 去网上查了一下webftp 发现是一个在线php文件管理系统 WebFTP——在线FTP工具:强大的PHP在线文件管理系统-時日 在这篇博客…

CentOS7平台命令安装Anaconda3、配置Python3开发环境

要在 CentOS 7 上安装 Anaconda3&#xff0c;您可以按照以下步骤进行操作&#xff1a; 1. 下载 Anaconda3 安装包&#xff1a; 首先&#xff0c;访问 Anaconda 官方网站以获取最新版本的 Anaconda3 安装包的下载链接。可以使用 wget 命令来下载安装包。请确保选择适用于 Cent…

数字IC设计系列----单端口RAM、双端口RAM

一、单端口RAM原理及实现 1.1、概念/原理 在内存空间中开辟出一段固定大小的内存用于存储数据&#xff0c;每一个数据所占的bit位称之为位宽&#xff0c;这段内存空间中数据的总数称之为深度。例如reg [7:0] mem [255:0]&#xff0c;这段内存空间中每一个数据的位宽为8bit&am…

postgresql用户和角色

postgresql用户和角色 简述创建角色角色属性登录特权超级用户创建数据库创建角色启动复制密码修改角色属性 对象授权撤销授权组和成员删除角色 简述 PostgreSQL 通过角色的概念来控制数据库的访问权限。角色又包含了两种概念&#xff0c;具有登录 权限的角色称为用户&#xff…

Nature Communications | 张阳实验室:端到端深度学习实现高精度RNA结构预测

RNA分子是基因转录的主要执行者&#xff0c;也是细胞运作的隐形功臣。它们在基因表达调控、支架构建以及催化活性等多个生命过程中都扮演着关键角色。虽然RNA如此重要&#xff0c;但由于实验数据的缺乏&#xff0c;准确预测RNA 的三维空间结构仍然是目前计算生物学面临的重大挑…

Flink 内存模型

Jobmanage内存模型 1G 1C 的配置 上图不够直观,用户大脑无法第一反应出内存构成。 Total Process Memory = JVM堆内存 + JVM堆外内存(堆外内存+ JVM元空间 +JVM自身运行内存) Total Flink Memory = JVM堆内存 + 堆外内存 参数控制: Total Process Memory 对应 jobmanag…

2023 “华为杯” 中国研究生数学建模竞赛(C题)深度剖析|数学建模完整代码+建模过程全解全析

华为杯数学建模C题 当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2021年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看研赛的C题呀~&#xff01; 问…

二十四、MySQL事务操作演示

1、事务 &#xff08;1&#xff09;事务简介&#xff1a; &#xff08;2&#xff09;实际操作方式&#xff1a; 在执行MySQL语句时&#xff0c;系统默认自动提交&#xff0c;但是语句一旦出现报错&#xff0c;就可能导致数据出现大规模错误&#xff0c;所以我们要做的就是&…

牛客网解题之跳台阶

10.3 跳台阶 题目链接 牛客网 题目描述 一只青蛙一次可以跳上 1 级台阶&#xff0c;也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 解题思路 当 n 1 时&#xff0c;只有一种跳法&#xff1a; 当 n 2 时&#xff0c;有两种跳法&#xff1a; 跳 n 阶台…

【强化学习】03 ——马尔可夫决策过程

文章目录 1. 马尔科夫决策过程(Markov Decision Process&#xff0c;MDP)1.1. 马尔科夫性质1.2. 状态转移矩阵1.3. 马尔可夫过程1.3.1. 一个简单的例子 2. 马尔可夫奖励过程2.1. 回报2.2. 价值函数 3. 马尔科夫决策过程3.1. MDP五元组3.2. 策略3.3. 价值函数3.3.1. 状态价值函数…

MYSQL——命令大全

1.创建数据库&#xff1a; CREAT E DATABASE [IF NOT EXISTS] DATABASE_NAME; 2.查看数据库&#xff1a; SHOW DATABASES; 3.进入数据库 USE DATABASE_NAME; 4.指定字符集&#xff08;character&#xff09;和校对规则&#xff08;collation&#xff09;创建数据库 CREA…

Spring面试题18:Spring中可以注入一个null和一个空字符串吗?Spring中如何注入一个java集合?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring中可以注入一个null和一个空字符串吗? 在Spring中是可以注入null和空字符串的。 注入null:可以使用@Value注解,将属性值设为null。例如:…

ChatGPT可以取代搜索引擎吗?

ChatGPT对于一些简单的问题&#xff0c;可以完美的完成任务。但是我让它写一篇完整的文章&#xff0c;看看它能否代替我进行写作地的时候&#xff0c;我确定它不能完全取代人类。 但是我们可以使用更多的指导来让AI在日常工作流程为我们工作&#xff0c;所以本文将讨论如何有效…

Spimes x5.0主题模板全开源源码

Spimes主题为博客、自媒体、资讯类的网站设计开发&#xff0c;自适应兼容手机、平板设备的团队&#xff0c;工作室门户主题&#xff0c;精心打磨的一处处细节。只为让您的站点拥有速度与优雅兼具的极致体验。小灯泡自媒体博客免授权 安装教程&#xff1a; 1.模板目录usr/them…