【UCB CS 61B SP24】Lecture 11 - Inheritance 4: Iterators, Object Methods学习笔记

本文内容为集合(Set)的介绍与使用,并通过数组手动实现集合,接着介绍了迭代器,使用迭代器我们能够更方便地遍历集合中的元素。

1. Set

1.1 Set介绍与Java实现类的使用

集合(Set)是一种常见的数据结构,用于存储一组唯一的不重复的元素。集合的核心特点是不允许重复元素,并且通常不保证元素的顺序。其核心特性如下:

  • 唯一性:集合中的元素是唯一的,不允许重复。如果尝试添加一个已经存在的元素,集合不会发生改变。
  • 无序性:集合通常不保证元素的顺序(除非使用有序集合实现,如 Java 中的 TreeSetLinkedHashSet)。
  • 动态性:集合的大小可以动态调整,支持添加、删除和查找操作。
  • 高效性:集合的实现通常基于哈希表或平衡树,因此添加、删除和查找操作的时间复杂度通常为 O ( 1 ) O(1) O(1) O ( l o g n ) O(log n) O(logn)

在 Java 中,Set 是一个不包含重复元素的集合接口。它是 java.util 包的一部分,继承自 Collection 接口。Java 已经默认提供了多个 Set 的实现类,常用的有:

(1)HashSet

基于哈希表实现,不保证元素的顺序,允许有 null 元素,插入、删除和查找操作的时间复杂度为 O ( 1 ) O(1) O(1),在 java.util.HashSet 包中:

Set<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Cherry");
System.out.println(hashSet);  // 输出可能是[Apple, Cherry, Banana],不保证顺序

(2)LinkedHashSet

继承自 HashSet,基于哈希表和链表实现,能够维护元素的插入顺序,允许有 null 元素,插入、删除和查找操作的时间复杂度为 O ( 1 ) O(1) O(1),在 java.util.LinkedHashSet 包中:

Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Apple");
linkedHashSet.add("Banana");
linkedHashSet.add("Cherry");
System.out.println(linkedHashSet);  // 输出一定是[Apple, Banana, Cherry]

(3)TreeSet

基于红黑树实现,元素按照自然顺序或指定的比较器进行排序,不允许有 null 元素,插入、删除和查找操作的时间复杂度为 O ( l o g n ) O(log n) O(logn),在 java.util.TreeSet 包中:

Set<String> treeSet = new TreeSet<>();
treeSet.add("Cherry");
treeSet.add("Banana");
treeSet.add("Apple");
System.out.println(treeSet);  // [Apple, Banana, Cherry]

Set 接口继承了 Collection 接口的所有方法,以下是一些常用的方法:

  • add(T item):将指定的元素添加到集合中(如果尚未存在)。
  • remove(T item):从集合中移除指定的元素(如果存在)。
  • contains(T item):判断集合中是否包含指定的元素。
  • size():返回集合中的元素数量。
  • isEmpty():判断集合是否为空。
  • clear():移除集合中的所有元素。
  • iterator():返回一个迭代器,用于遍历集合中的元素。
package CS61B.Lecture11;import java.util.HashSet;
import java.util.Set;public class SetExample {public static void main(String[] args) {Set<String> fruits = new HashSet<>();fruits.add("Apple");fruits.add("Banana");fruits.add("Cherry");// 尝试添加重复元素boolean isAdded = fruits.add("Apple");System.out.println(isAdded);  // false// 遍历集合for (String fruit : fruits)System.out.println(fruit);// 检查元素是否存在System.out.println(fruits.contains("Banana"));  // true// 移除元素fruits.remove("Cherry");System.out.println(fruits);  // [Apple, Banana]}
}

1.2 手动实现ArraySet

现在我们尝试用数组自己实现一个集合,代码类似之前实现的 AList

package CS61B.Lecture11;public class ArraySet<T> {private T[] items;private int size;private int capacity;private void expandCapacity() {this.capacity *= 2;T[] newItems = (T[]) new Object[this.capacity];System.arraycopy(this.items, 0, newItems, 0, this.size);this.items = newItems;}public ArraySet() {this.capacity = 2;this.items = (T[]) new Object[this.capacity];this.size = 0;}public int size() {return this.size;}public boolean contains(T item) {for (int i = 0; i < this.size; i++)if (this.items[i].equals(item)) return true;return false;}public void add(T item) {if (this.contains(item)) return;if (this.size == this.capacity) this.expandCapacity();this.items[this.size++] = item;}public static void main(String[] args) {ArraySet<String> fruits = new ArraySet<>();fruits.add("Apple");fruits.add("Banana");fruits.add("Cherry");fruits.add("Apple");System.out.println(fruits.size());  // 3}
}

2. 迭代器

2.1 增强型For循环

在前面使用 Java 的 Set 实现类的例子中我们用的遍历方法是这样的:

for (String fruit : fruits)

这被称为增强型 For 循环,我们手动实现的 ArraySet 并不能这样遍历,这是怎么实现的?其实这种循环是由以下代码组成的:

Iterator<String> it = fruits.iterator();
while (it.hasNext()) {String fruit = it.next();// Do something ...
}

2.2 Iterator & Iterable

上面这段代码中的 Iterator 称为迭代器,在 java.util.Iterator 中定义,是一个用于遍历集合(如 ListSetMap 等)中元素的接口,它提供了两个核心方法:hasNext()next()

  • boolean hasNext():检查集合中是否还有更多的元素可以遍历。若集合中还有未遍历的元素则返回 true,已经遍历完所有元素则返回 false
  • T next():返回集合中的下一个元素,每次调用 next() 都会自动将迭代器的指针移动到下一个元素。如果集合中没有更多的元素(即 hasNext() 返回 false),调用 next() 会抛出 NoSuchElementException 异常。因此在调用 next() 之前,通常需要先调用 hasNext() 进行检查。

因此我们的 ArraySet 中必须能够通过 iterator() 方法创建并返回一个自己的 Iterator 对象,并且这个对象具有 hasNext()next() 方法,此外还需要让我们的 ArraySet 实现 Iterable 接口(该接口具有 forEach() 方法),这样才能让 Java 知道我们这个类的对象是可以用迭代器遍历的:

package CS61B.Lecture11;import org.jetbrains.annotations.NotNull;import java.util.Iterator;public class ArraySet<T> implements Iterable<T> {private T[] items;private int size;private int capacity;private void expandCapacity() {this.capacity *= 2;T[] newItems = (T[]) new Object[this.capacity];System.arraycopy(this.items, 0, newItems, 0, this.size);this.items = newItems;}public ArraySet() {this.capacity = 2;this.items = (T[]) new Object[this.capacity];this.size = 0;}public int size() {return this.size;}public boolean contains(T item) {for (int i = 0; i < this.size; i++)if (this.items[i].equals(item)) return true;return false;}public void add(T item) {if (this.contains(item)) return;if (this.size == this.capacity) this.expandCapacity();this.items[this.size++] = item;}private class ArraySetIterator implements Iterator<T> {private int pos;public ArraySetIterator() {this.pos = 0;}@Overridepublic boolean hasNext() {return this.pos < size;}@Overridepublic T next() {T item = items[this.pos];this.pos++;return item;}}public @NotNull Iterator<T> iterator() {return new ArraySetIterator();}public static void main(String[] args) {ArraySet<String> fruits = new ArraySet<>();fruits.add("Apple");fruits.add("Banana");fruits.add("Cherry");fruits.add("Apple");System.out.println(fruits.size());  // 3for (String fruit : fruits)System.out.print(fruit + " ");  // Apple Banana Cherry}
}

3. 优化ArraySet

3.1 toString()

toString() 方法在之前的例子中我们已经重写过了,该方法提供对象的字符串表示形式,当我们要输出某个对象时 System.out.println(Object obj),这时会调用 obj.toString() 方法,如果没有重写这个方法那么默认返回的是 类名@地址 的形式。现在我们再重写一遍 toString()

package CS61B.Lecture11;import org.jetbrains.annotations.NotNull;import java.util.Iterator;public class ArraySet<T> implements Iterable<T> {...@Overridepublic String toString() {StringBuilder sb = new StringBuilder("[");for (T item : this) sb.append(item + ", ");if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());sb.append("]");return sb.toString();}public static void main(String[] args) {ArraySet<String> fruits = new ArraySet<>();fruits.add("Apple");fruits.add("Banana");fruits.add("Cherry");fruits.add("Apple");System.out.println(fruits.size());  // 3System.out.println(fruits);  // [Apple, Banana, Cherry]}
}

3.2 equals()

Java 中的 == 是按比特位比较两个变量的数值,而不会比较两个地址所分别指向的两个对象中的内容是否相等,先看下面这段代码:

package CS61B.Lecture11;import java.util.ArrayList;
import java.util.Arrays;public class EqualsExample {public static void main(String[] args) {ArrayList<Integer> l1 = new ArrayList<>(Arrays.asList(1, 2, 3));ArrayList<Integer> l2 = new ArrayList<>(Arrays.asList(1, 2, 3));System.out.println(l1 == l2);  // falseSystem.out.println(l1.equals(l2));  // true}
}

因为 l1l2 是两个不同列表对象的引用地址,如下图所示,因此用 == 去比较这两个引用那肯定是不相等的:

在这里插入图片描述

如果想要比较自定义类的两个对象的内容是否相等,需要重写 equals(Object obj) 方法,该方法也是所有类从 Object 那边继承过来的:

package CS61B.Lecture11;import org.jetbrains.annotations.NotNull;import java.util.Iterator;public class ArraySet<T> implements Iterable<T> {...@Overridepublic boolean equals(Object obj) {// 判断obj是否是ArraySet的实例,如果是则将obj转换成ArraySet类型的对象arraySetif (obj instanceof ArraySet arraySet) {if (this.size() != arraySet.size()) return false;for (T item : this)if (!arraySet.contains(item)) return false;return true;}return false;}public static void main(String[] args) {ArraySet<Integer> s1 = new ArraySet<>();ArraySet<Integer> s2 = new ArraySet<>();for (int i = 1; i <= 3; i++) {s1.add(i);s2.add(i);}System.out.println(s1 == s2);  // falseSystem.out.println(s1.equals(s2));  // true}
}

注意我们用到了 obj instanceof ArraySet arraySet,这是 instanceof 关键字在 Java 14 中引入的新特性,该特性在 Java 16 中正式成为标准特性,被称为模式匹配(Pattern Matching),能够避免手动类型转换可能导致的错误(如忘记转换或转换错误)。

在 Java 14 之前,如果你想检查一个对象是否是某个类的实例,并对其进行类型转换,通常需要写两行代码:

if (obj instanceof ArraySet) {ArraySet arraySet = (ArraySet) obj;  // 显式类型转换// 使用arraySet...
}

从 Java 14 开始,你可以将 instanceof 检查和类型转换合并为一行代码:

if (obj instanceof ArraySet arraySet) {// 直接使用arraySet...
}

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

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

相关文章

sessionStorage问题的思考和解决

通过前端访问成功&#xff0c;直接访问后端接口失败。思考的过程、问题的解决Session和sessionStorage 通过前端访问成功&#xff0c;直接访问后端接口失败。 做黑马点评的使用Redis代替Session实现短信登录的功能时&#xff0c;遇到了一个问题&#xff1a; 就是我设计好代码后…

YOLO11改进-模块-引入混合结构模块Mix Structure Block 提高多尺度、小目标

在图像去雾领域&#xff0c;传统的基于卷积神经网络&#xff08;CNN&#xff09;和 Transformer 的方法存在局限性。CNN 方法大多存在感受野不足的问题&#xff0c;限制了单个像素在神经网络中的参考范围&#xff0c;部分考虑大感受野的 CNN 方法又忽略了图像的多尺度特性&…

MySQL主从架构

MySQL主从架构 MySQL REPLICATION 在实际生产环境中&#xff0c;如果对数据库的读和写都在一个数据库服务器中操作。无论是在安全性、高可用性&#xff0c;还是高并发等各个方面都是完全不能满足实际需求的&#xff0c;因此&#xff0c;一般来说都是通过主从复制&#xff08;…

6层高速PCB设计入门第1~10讲

第一讲 课程介绍 无痛入门&#xff01;6层高速PCB设计&#xff01;_哔哩哔哩_bilibili 第二讲 逻辑派原理图分析 开发板资料文档&#xff1a;https://wiki.lckfb.com/zh-hans/fpga-ljpi/ 最需要注意的信号就是FPGA与DDR3、HDMI交互的信号&#xff0c;其次是GD32读写TF Card的…

Mesh自组网技术及应用

前言&#xff1a; Mesh自组网随着无线技术发展&#xff0c;在消费领域最近比较有热度。当然应用的场景不限于普通消费领域&#xff0c;在工业、军事领域被也是越来越重要。 一、什么是无线Mesh技术 1.1 无线自组网概念 无线Mesh是一种智能、自组织、多跳、移动、对等、去中心…

Python游戏编程之赛车游戏6-3

1 “敌人”汽车类的创建 在创建玩家汽车类之后&#xff0c;接下来创建“敌人”汽车类。“敌人”汽车类与玩家类一样&#xff0c;也是包含两个方法&#xff0c;一个是__init__()&#xff0c;另一个是move()。 1.1 __init__()方法 “敌人”汽车类的__init__()方法代码如图1所示…

垂类大模型微调(二):使用LLaMA-Factory

上一篇博文和大家一起安装了LLaMA-Factory工具,并下载了大模型在上面进行了简单的加载和推理,今天尝试通过LoRa技术对大模型进行微调; 一、训练集准备 1.1 介绍训练集结构 这里演示对Qwen2.5-0.5B-Instruct-GPTQ-Int4模型进行LoRA微调, 大家可以根据垂类大模型微调(一)…

什么是MySql的主从复制(主从同步)?

主页还有其他面试题总结&#xff0c;有需要的可以去看一下&#xff0c;喜欢的就留个三连再走吧~ 1.什么是MySql的主从复制原理&#xff1f; 主从复制的核心就是二进制binlog&#xff08;DDL&#xff08;数据定义语言&#xff09;语句和DML&#xff08;数据操纵语言&#xff09…

坐标变换及视图变换和透视变换(相机透视模型)

文章目录 2D transformationScaleReflectionShear&#xff08;切变&#xff09;Rotation around originTranslationReverse变换顺序复杂变换的分解 齐次坐标&#xff08;Homogenous Coordinates&#xff09;3D transformationScale&TranslationRotation Viewing / Camera t…

文字语音相互转换

目录 1.介绍 2.思路 3.安装python包 3.程序&#xff1a; 4.运行结果 1.介绍 当我们使用一些本地部署的语言模型的时候&#xff0c;往往只能进行文字对话&#xff0c;这一片博客教大家如何实现语音转文字和文字转语音&#xff0c;之后接入ollama的模型就能进行语音对话了。…

Unity Shader 学习13:屏幕后处理 - 使用高斯模糊的Bloom辉光效果

目录 一、基本的后处理流程 - 以将画面转化为灰度图为例 1. C#调用shader 2. Shader实现效果 二、Bloom辉光效果 1. 主要变量 2. Shader效果 &#xff08;1&#xff09;提取较亮区域 - pass1 &#xff08;2&#xff09;高斯模糊 - pass2&3 &#xff08;3&#xff…

PING命令TTL解析

在 ping 命令中&#xff0c;TTL&#xff08;Time to Live&#xff0c;生存时间&#xff09; 是 IP 数据包的核心字段之一&#xff0c;用于控制数据包在网络中的生命周期。以下是针对 TTL 的简明解析&#xff1a; 1. TTL 的核心作用 防循环机制&#xff1a;TTL 是一个计数器&a…

Linux 第三次脚本作业

源码编译安装httpd 2.4&#xff0c;提供系统服务管理脚本并测试&#xff08;建议两种方法实现&#xff09; 一、第一种方法 1、把 httpd-2.4.63.tar.gz 这个安装包上传到你的试验机上 2、 安装编译工具 (俺之前已经装好了&#xff09; 3、解压httpd包 4、解压后的httpd包的文…

(七)趣学设计模式 之 适配器模式!

目录 一、 啥是适配器模式&#xff1f;二、 为什么要用适配器模式&#xff1f;三、 适配器模式的实现方式1. 类适配器模式&#xff08;继承插座 &#x1f468;‍&#x1f469;‍&#x1f467;‍&#x1f466;&#xff09;2. 对象适配器模式&#xff08;插座转换器 &#x1f50c…

【NLP】注意力机制

目录 一、认识注意力机制 1.1 常见注意力计算规则 1.2 注意力机制的作用 1.3 注意力机制代码实现 二、注意力机制原理 2.1 attention计算过程 2.2 attention的计算逻辑 2.3 有无attention模型对比 2.3.1 无attention机制的模型 2.3.2 有attention机制的模型 三、Se…

Spring Boot 整合 Druid 并开启监控

文章目录 1. 引言2. 添加依赖3. 配置数据源4. 开启监控功能5. 自定义 Druid 配置&#xff08;可选&#xff09;6. 访问监控页面7. 注意事项8. 总结 Druid 是一个由阿里巴巴开源的高性能数据库连接池&#xff0c;它不仅提供了高效的连接管理功能&#xff0c;还自带了强大的监控…

红帽7基于kickstart搭建PXE环境

Kickstart 文件是一种配置文件&#xff0c;用于定义 Linux 系统安装过程中的各种参数&#xff0c;如分区、网络配置、软件包选择等。system-config-kickstart 提供了一个图形界面&#xff0c;方便用户快速生成这些配置文件。 用户可以通过图形界面进行系统安装的详细配置&…

C/C++跳动的爱心

系列文章 序号直达链接1C/C李峋同款跳动的爱心2C/C跳动的爱心3C/C经典爱心4C/C满屏飘字5C/C大雪纷飞6C/C炫酷烟花7C/C黑客帝国同款字母雨8C/C樱花树9C/C奥特曼10C/C精美圣诞树11C/C俄罗斯方块小游戏12C/C贪吃蛇小游戏13C/C孤单又灿烂的神14C/C闪烁的爱心15C/C哆啦A梦16C/C简单…

MongoDB 简介

MongoDB 是一种高性能、开源的 NoSQL 数据库&#xff0c;以其灵活的文档模型和强大的扩展性而闻名。 1.MongoDB 是什么 MongoDB 是一种 NoSQL 数据库&#xff0c;采用 文档模型 存储数据&#xff0c;支持灵活的 JSON 格式文档。它无需预定义表结构&#xff0c;能够动态调整数据…

记录首次安装远古时代所需的运行环境成功npm install --save-dev node-sass

最开始的报错&#xff1a; 最后根据报错一步步 安装所需要的pythong之类的环境&#xff0c;最后终于成功了&#xff0c;得以让我在github上拉的vuehr项目&#xff08;狗头18年还是20年的远古项目&#xff09;成功本地运行&#xff0c;最后附上本地运行成功的贴图。如果大家也在…