数据结构学习基础和从包装类缓存到泛型擦除的避坑指南

目录

1.数据结构的概念和算法

1.1 数据结构的概念 

1.2 数据结构的集合框架

1.3 算法 

 1.3.1 时间复杂度

1.3.2 空间复杂度 

 2.包装类

2.1 为什么需要包装类?

 2.2 装箱和拆箱

 3. 初识泛型

 3.1 认识泛型

3.2 泛型类的使用 

3.3 泛型的编译

3.4 通配符

 3.4.1 无界通配符

3.4.2 上界通配符 

3.4.3 下界通配符 


1.数据结构的概念和算法

1.1 数据结构的概念 

数据结构是程序的骨架,算法是程序的灵魂。什么是数据结构?

在《大话数据结构》这本书中,作者指出:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。也就是说,数据结构是计算机中存储、组织数据的方式。就像现实生活中我们使用文件夹整理文档、用书架分类书籍一样,程序也需要合理的方式管理数据。

1.2 数据结构的集合框架

数据结构的主要集合框架为上图。

学习数据结构,就要了解以上集合框架图的关系和每个具体的类是什么样的数据结构。这个图做一个简单的了解即可,不必刻意深记,在后面的学习中都会详细介绍。

1.3 算法 

 算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。

怎么衡量一个算法的好坏?这就要谈到算法的效率。算法的效率分为两种,一是时间效率,也称为时间复杂度,二是空间效率,也称为空间复杂度。二者通常用大O的渐进表示法来计算。

 1.3.1 时间复杂度

时间复杂度(Time Complexity) 是衡量算法执行时间随输入规模增长的变化趋势的指标。它不计算具体的运行时间,而是描述算法在最坏或平均情况下执行基本操作的次数与输入规模 n 的关系,通常用 大O符号(Big-O Notation) 表示。 

 大O渐进法怎么表示?

  1. 用常数 1 代替运行时间中的所有加法常数 
  2. 保留最高项,并且忽略最高项的系数

 怎么理解大O渐进表示?

通常在衡量一个算法的效率时,都是根据最坏情况去衡量。当然会有一些算法存在平均情况和最后情况。

最坏情况:任意输入规模的最大允许次数

平均情况:任意输入规模的期望运行次数

最坏情况:任意输入规模的最小运行次数 

比如在一个长度为n的整数 升序 数组中,查找数组中是否存在某一个值时,如果按照线性查找的方式(从前到后或者从后到前)查找,那么最坏的可能是最后一个数组元素才是需要查找的值,或者这个数组本身就没有这个值的时候,其时间复杂度便是达到 O(n) ,如果第一个元素就是要查找的值,这个时候就可以达到0(1) 。如果使用二分查找的方式(每次取这个数组的中间元素进行比较)进行查找,如果中间元素的值大于需要查找的值,则说明要查找的值在这个中间元素的左边,那么下次查找只需要在左边进行查找,依次类推,直到找到与需要查找的值相等或者最后一个元素为止, 这种方式时间复杂度最坏情况只有, 同理,如果第一个元素就是要查找的值,这个时候就可以达到0(1) 。

1.3.2 空间复杂度 

 空间复杂度(Space Complexity)是衡量算法在运行过程中 临时占用的内存空间 随输入规模(n)增长的变化趋势。与时间复杂度类似,空间复杂度也用 大O表示法(Big-O Notation) 描述。

有一些说法是:空间复杂度就是计算变量的个数 。

这个说法并不完全正确,空间复杂度计算的是 算法运行过程中额外占用的内存空间,而不仅仅是变量的个数。变量个数是影响因素之一,还需考虑:

  1. 变量的规模(如数组、链表、递归栈等占用的空间与输入规模 n 相关)。

  2. 数据结构的内存占用(如 HashMap 比 Array 更占空间)。

  3. 递归调用的栈空间(每次递归都会占用额外的栈帧)。

比如曾经在递归的学习中,计算第 n 项的斐波那契数时,当项数达到一定值时,就会发生栈溢出。这是因为每一次递归时,都会产生一个临时的内存空间来存储参数和返回地址等。

 2.包装类

2.1 为什么需要包装类?

在Java中,由于基本类型并不是继承 Object 类,为了在泛型代码中可以支持基本类型,Java为8种基本数据类型提供的对应的引用类型(对象类型),用于将基本类型“包装”成对象。其基本数据类型 的对应的包装类如下:

 

 可以看出,出来 int char 的包装类分别对应 Integer Character ,其余的基本数据类型对应的包装类都是首字母大写。

那么,为什么需要包装类呢?

1. Java是面向对象的语言,但是基本数据类型并不是对象。包装类的作用:

  • 存储在集合中:如 List<Integer>(集合只能存对象,不能存基本类型)

  • 调用对象方法:如 Integer.parseInt(String s)

  • 支持泛型:泛型类型必须为对象(如 ArrayList<Integer>),而 ArrayList<int> 是错误的

如果没有包装类,就无法使用 ArrayListHashMap 等集合存储基本类型。

2. 允许null

基本类型不能为null,但包装类可以表示数据缺失或未初始化。

3. 提供丰富的工具方法

Integer.toBinaryString() ,可以把一个 int 类型的数据转为二进制字符串,Character.isLetter() ,判断一个字符是否为字母等

4. 自动装箱和拆箱

 2.2 装箱和拆箱

  • 装箱(Boxing:将基本数据类型转换为对应的包装类对象。分为手动装箱自动装箱

public class Main {public static void main(String[] args) {int a = 10;//装箱操作--手动装箱Integer A11 = Integer.valueOf(a);Integer A12 = new Integer(a);//这种创建新对象已经过时,但是可以用//装箱操作--自动装箱Integer A21 = a;}
}
  • 拆箱(Unboxing:将包装类对象转换回基本数据类型。分为手动拆箱自动拆箱

public class Main {public static void main(String[] args) {Integer a = 10;//拆箱操作--手动拆箱int A = a.intValue();////拆箱操作--自动拆箱int B = a;//编译器自动转换}
}

注意事项:

1. 前面提到包装类允许有 null ,但是如果把包装类换成基本类型时,就会抛出异常NullPointerException 

2. 部分包装类(如Integer)缓存 -128 ~ 127 的值,超出范围会新建对象: 

 观察以下代码:

public class Main {public static void main(String[] args) {Integer a = 100;Integer b = 100;Integer c = 200;Integer d = 200;System.out.println(a == b);//结果1:System.out.println(c == d);//结果2:System.out.println(a.equals(b));//结果3:System.out.println(c.equals(d));//结果4:}
}

 输出结果:

 为什么 c 和 d 都等于200时,输出结果却是 false呢?这是因为Java 对 Integer 包装类设计了缓存机制(范围默认是 -128~127 ),当超出这个范围后,就会 new 一个新的对象,而对于对象的比较,不能用 == ,要用 equals() 或者对其拆箱再用 == 进行比较。

 3. 初识泛型

 3.1 认识泛型

关于泛型的概念,在《Java编程思想》中这样介绍:一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。也就是说,泛型的出现,是为了适用于许多种的类型。

当需要什么样的类型时,就传递什么类型即可,包括 Integer、Character 等,也可以是自定义的类,使用泛型,可以在编译时检查类型匹配,避免运行时发生异常 ClassCastException

语法:

class  泛型名称 <类型形参列表> {

       //使用的类型参数

比如在实现一个通用的哈希桶(在后面会学习到)时,就可以有以下代码:

public class HashBuck<K,V> {static class Node<K, V> {public K key;public V val;public Node<K, V> next;public Node(K key , V val) {this.key = key;this.val = val;}}//往后实现具体方法等....
}

 泛型类用尖括号 <类型形参列表> 来表示,类型形参一般用大写字母表示,常用的名称有:

  • E:表示Element
  • K:表示Key
  • V:表示Value
  • N:表示Number
  • T:表示Type

3.2 泛型类的使用 

语法 :

泛型类<类型实参> 变量名;//定义一个泛型类引用

new 泛型类<类型实参>(构造方法实参);//实例化一个泛型类对象,实例化的泛型实参和构造方法可以没有  

 泛型类也是一个类,在使用泛型时要 new 一个新的对象。如

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

3.3 泛型的编译

 Java 泛型的核心在于编译时的类型检查运行时的类型擦除

类型擦除的原则(和继承类似):

  • 无边界类型参数

        对于没有边界的泛型类(如<T>),在编译时都会默认上界是 Object 类,也就是上 T 全部变为Object ,因为 Object 类 是所有类的父类

  • 有上界的类型参数

        对于有上界的泛型类(如T  extends  Numbers),编译时 T 会替换成 Number。

  • 泛型方法

        编译后,方法签名中的 <T>被移除,参数和返回值的T替换为Object或上界。

// 源码(编译前)
public class Box<T> {private T value;public void set(T value) {this.value = value;}public T get() {return value;}
}// 字节码(编译后,等效代码)
public class Box {private Object value; // T被擦除为Objectpublic void set(Object value) {this.value = value;}public Object get() {return value;}
}

 因此,List<String>和 List<Integer>的类对象是相同的,因为类型擦除,编译后<String><Integer>)将被替换为原始类型(Object 或指定的上界类型),运行时均为List.class

3.4 通配符

 通配符(?)是 Java 泛型中的一种特殊语法,用于增强泛型的灵活性,主要解决泛型类型在继承关系、未知类型、可以接收所有的泛型类型但又不希望他人随意修改场景下的匹配问题。

 3.4.1 无界通配符<?>

表示未知类型,可以匹配任何的泛型类型,当方法需要接受任意类型的泛型对象但不关心具体类型时使用。

3.4.2 上界通配符 <? extends T> 

表示T 或其子类,用于放宽泛型的读取限制 ,当需要从泛型对象中安全读取数据,且数据是 T 的子类时使用。

3.4.3 下界通配符 <? super T> 

表示T 或其父类,用于放宽泛型的写入限制,当 需要向泛型对象中安全写入数据,且数据是 T 的父类。

何时使用上界/下界通配符?

  • 需要读取数据时用 <? extends T>

  • 需要写入数据时用 <? super T>

  • 既读又写时用具体类型参数 <T>

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

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

相关文章

网络安全基础知识【6】

什么是防火墙1.防火墙指的是一个由软件和硬件设备组合而成、在内部网和外部网之间、 专用网与公共网之间的界面上构造的保护屏障 2.防火墙实际上是一种隔离技术 3.防火墙重要的特征是增加了区域的概念防火墙的定义 隔离可信与不可信网络的设备/软件&#xff0c;基于策略控制流量…

Apache Doris数据库——大数据技术

Apache Doris一、简介1.1、Apache Doris简介1.2、Apache Doris 与传统大数据架构相比1.3、doris是java团队掌控大数据能力最优选择1.4、 OLTP&#xff08;在线事务处理&#xff09; 与 OLAP&#xff08;在线分析处理&#xff09;1.5、发展历程1.6、应用现状1.7、整体架构1.7.1、…

Conda和pip的使用记录

Conda和pip的使用记录一、创建新的 Conda 环境二、激活环境三、安装其他包&#xff08;可选&#xff09;四、查看已有环境五、删除环境&#xff08;可选&#xff09;⚙️ Conda 下载缓慢的解决方案&#xff08;推荐使用国内镜像&#xff09;&#x1f527; 方法一&#xff1a;**…

详解Python标准库之互联网数据处理

详解Python标准库之互联网数据处理 在互联网时代&#xff0c;数据的产生、传输和处理无处不在。从电子邮件的收发到 API 接口的数据交换&#xff0c;从二进制数据的编码到 MIME 类型的识别&#xff0c;Python 标准库提供了一整套强大的工具集&#xff0c;帮助开发者轻松应对各种…

适 配 器 模 式

前阵子&#xff0c;笔者在网上淘来一个二手显示屏来搭配我装好的主机&#xff0c;但是送到手上后我却找不到电源适配器的踪迹。于是我就在家找了根电源线接上了显示屏&#xff0c;倒是能亮&#xff0c;就是屏幕闪得和机关枪似的。这是因为我的显示屏需要12V的供电&#xff0c;我…

智慧零售商品识别准确率↑32%:陌讯多模态融合算法实战解析

原创声明本文为原创技术解析&#xff0c;核心技术参数与架构设计引用自《陌讯技术白皮书》&#xff0c;禁止任何形式的未经授权转载。一、行业痛点&#xff1a;智慧零售的 "看得见的障碍"在智慧零售场景中&#xff0c;从自助结算终端到智能货架管理&#xff0c;计算机…

Linux系统编程-gcc(黑马笔记)

1 gcc的编译流程gcc编译的整个过程并且整个过程下来的每个过程。并且给出了每个阶段产物和gcc命令。1.1 数据段合并其实就是因为“块” 一次是读多个字节而不是一个字节&#xff0c;所以会将一些地址段合并从而提升效率1.2 地址回填这张图也有些问题&#xff0c;正确的结论是:地…

Git踩坑

文章目录前言❓问题分析&#xff1a;为什么你的提交会“覆盖”别人的代码&#xff1f;✅ 正确的代码提交流程&#xff08;结合你原文的说明&#xff09;**1. 确认自己在正确的分支上****2. 从主开发分支&#xff08;如 dev&#xff09;拉取最新代码并合并****3. 解决冲突&#…

sqli-labs:Less-20关卡详细解析

1. 思路&#x1f680; 本关的SQL语句为&#xff1a; $sql"SELECT * FROM users WHERE username$cookee LIMIT 0,1";注入类型&#xff1a;字符串型&#xff08;单引号包裹&#xff09;、GET操作提示&#xff1a;参数需以闭合关键参数&#xff1a;cookee php输出语句…

基于LevitUnet的超声图像分割

完整项目包获取&#xff1a;点击文末名片本项目旨在开发一个基于深度学习的图像分割模型&#xff0c;专门用于处理医学或遥感领域的图像数据&#xff08;以 TIFF 格式存储&#xff09;。通过结合 LeViT&#xff08;基于 Vision Transformer 的轻量模型&#xff09;和 U-Net 架构…

Java 17 新特性解析与代码示例

Java 17 新特性解析与代码示例 文章目录Java 17 新特性解析与代码示例引言1. 密封类&#xff08;JEP 409&#xff09;1.1. 介绍1.2. 详细说明1.3. 代码示例1.4. 与之前功能的对比1.5. 使用场景1.6. 总结2. switch 模式匹配&#xff08;预览&#xff0c;JEP 406&#xff09;2.1.…

SQL中的GROUP BY用法

GROUP BY 是 SQL 中用来“按列分组”的子句。 它把相同值的行分到同一个组&#xff0c;然后通常配合聚合函数&#xff08;COUNT, SUM, AVG, MAX, MIN 等&#xff09;对每个组做统计&#xff0c;最终每组只返回一行结果。✅ 1. 基本语法 SELECT 列1, 列2, 聚合函数(列3) FROM 表…

AI Agent开发学习系列 - LangGraph(10): 带有循环的Looping Graph(练习解答)

在AI Agent开发学习系列 - LangGraph(9): 带有循环的Looping Graph中&#xff0c;我们学习了如何创建带有循环的Looping Graph。为了巩固学习&#xff0c;我们来做一个练习。 用LangGraph创建如下图的一个Agent: 要求&#xff1a; 输入玩家姓名通过输入的上限值和下限值之间…

【保姆级 - 大模型应用开发】DeepSeek R1 本地部署全攻略:Ollama + vLLM + PyTorch 多选方案

DeepSeek R1 本地部署全攻略&#xff1a;Ollama vLLM PyTorch 多选方案 想部署 DeepSeek-R1 模型到本地&#xff0c;开启高性能推理体验&#xff1f;本文汇总了 Ollama、vLLM 及原生 PyTorch 的部署方法&#xff0c;适合不同开发者需求。 &#x1f3af; 下载模型 (必做) ----…

使用 Vive Tracker 替代 T265 实现位姿获取(基于 Ubuntu + SteamVR)

在Dexcap这篇工作列出第二版硬件清单时&#xff0c;我注意到其使用 Vive Tracker 替代 Intel T265 来获取位姿数据&#xff0c;对这个东西的性能感到好奇&#xff0c;最近因为需要跟进相关工作&#xff0c;参与了一部分实现&#xff0c;由于这方面的中文资料相对较少&#xff0…

博物馆 VR 导览:图形渲染算法+智能讲解技术算法实现及优化

本文面向博物馆数字化开发技术员、VR 系统工程师等技术同仁们&#xff0c;聚焦图形渲染算法在博物馆 VR 导览中的核心应用&#xff0c;解决虚拟展馆还原精度不足、多终端适配卡顿、智能讲解触发延迟等实际技术问题。如有项目合作及技术交流欢迎私信作者~一、VR导览技术痛点1.3D…

zset 中特殊的操作

首先 zset 与我们常规的 redis 操作有所不同, 这里的时间复杂度基本都是 O(log N) 起步的 目录 1. zcount 2. zpopmax 1. zcount zcount key min max : 这里求的是 key 中下标在 min 和 max 之间的 元素的数量, 这里是比区间 我们要是想排除端点, 就需要加上 ( , 无论是…

KSP与ASM深度对比:原理、性能与使用场景

一、核心目的差异1. KSP&#xff08;Kotlin Symbol Processing&#xff09;核心目的&#xff1a;在编译时生成新代码&#xff0c;解决样板代码问题(操作对象:.kt源文件编译过程中的中间表示)主要场景&#xff1a;自动生成DI&#xff08;依赖注入&#xff09;配置代码创建路由映…

【LLM】如何在Cursor中调用Dify工作流

这篇文章将通过一个接口文档知识库示例&#xff0c;带你了解如何在 Cursor 中通过 Mcp Server 调用 Dify 平台配置的工作流。 1. 准备工作 需要准备文本生成模型、向量模型、Rerank 模型&#xff08;可选&#xff09;&#xff0c;这些都可以在 阿里云百炼平台 申请免费使用额度…

L1、L2正则化的几何解释

L2正则化: 图中用几何方式形象地解释了 Ridge 回归&#xff08;L2正则化&#xff09;的原理。 ① 阴影圆&#xff1a;可以理解为&#xff08;w1^2 w2^2&#xff09;​≤R^2&#xff0c;圆周表示目标函数的约束线&#xff0c;这个圆表示了我们的参数 (w1,w2)可以活动的范围。 …