Java 与 Go:可变数组

可变数组(也称为动态数组)是一种可以在运行时动态增加或减少其大小的数据结构。由于其动态分配大小,灵活性增删改查,动态地管理内存(在需要时动态分配内存空间,以适应数据结构的大小变化,而不会浪费过多的内存空间)以及具有良好的性能特性。因此在许多编程任务中都是非常有用的数据结构。而在Java中最承担该角色的是某些List接口实现类,Go则有切片。那么今天我们来聊一聊ArrayList和SLICE

ArrayList

当提及可变数组时,Java中的ArrayList是一个常见的实现。下面是关于ArrayList的详细信息:

1. 数据结构:

ArrayList 是 Java 中的动态数组实现。它是基于数组的数据结构,可以根据需要自动增长和缩小。ArrayList 实现了 List 接口,因此可以存储任意类型的对象,并且可以通过索引访问这些对象。

2. 功能特点:

  • 动态大小: ArrayList 的大小可以动态地增长或缩小。当添加元素时,如果底层数组已满,ArrayList 会自动重新分配更大的内存空间来容纳更多的元素。
  • 随机访问: 由于 ArrayList 基于数组实现,因此支持通过索引进行快速随机访问。这使得访问、修改或删除元素的操作具有 O(1) 的时间复杂度。
  • 插入和删除: ArrayList 支持在任意位置插入和删除元素。但是,在列表中间或开头进行插入或删除操作时,需要将后续元素向后移动,因此具有较高的时间复杂度。
  • 允许重复元素: ArrayList 允许存储重复的元素,并且可以按照插入顺序保持元素的顺序。
  • 不是线程安全的: ArrayList 不是线程安全的,如果多个线程同时访问或修改 ArrayList,可能会导致不确定的行为。如果需要在多线程环境中使用,可以考虑使用 Collections.synchronizedList 方法来获得一个线程安全的 ArrayList。

3. 构造方式:

在 Java 中,ArrayList 可以通过多种构造方式进行实例化。下面是一些常用的构造方式:

  • 默认构造函数:
//这将创建一个空的 ArrayList,初始容量为 10。
ArrayList<E> list = new ArrayList<>();下面是一个例子
// 创建一个空的 ArrayList
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("Apple");
list.add("Banana");
list.add("Orange");// 输出 ArrayList 内容
System.out.println("ArrayList elements: " + list);

  • 指定初始容量的构造函数:
//这将创建一个空的 ArrayList,其初始容量由 initialCapacity 参数指定。
//如果知道大致要存储的元素数量,可以使用这个构造函数来提高性能,避免频繁的扩容操作。
ArrayList<E> list = new ArrayList<>(initialCapacity);//举个例子
// 创建一个初始容量为 20 的 ArrayList
ArrayList<Integer> list = new ArrayList<>(20);// 添加元素
for (int i = 1; i <= 20; i++) {list.add(i);
}// 输出 ArrayList 内容
System.out.println("ArrayList elements: " + list);
  • 使用 Collection 来初始化 ArrayList 的构造函数:
//这将创建一个包含指定集合中的元素的 ArrayList。元素将按照集合的迭代器返回的顺序排列。
ArrayList<E> list = new ArrayList<>(Collection<? extends E> c);//举个例子
String[] array = {"Apple", "Banana", "Orange"};// 使用 ArrayList 构造函数从 List 初始化 ArrayList
ArrayList<String> list = new ArrayList<>(Arrays.asList(array));// 输出 ArrayList 内容
System.out.println("ArrayList elements: " + list);
  • 从另一个 ArrayList 复制元素的构造函数:
//这将创建一个包含指定 ArrayList 中的元素的新 ArrayList。元素的顺序将与原始 ArrayList 保持一致。
ArrayList<E> list = new ArrayList<>(ArrayList<? extends E> c);//举个例子
// 创建一个包含指定集合中的元素的 ArrayList
ArrayList<String> sourceList = new ArrayList<>();
sourceList.add("Apple");
sourceList.add("Banana");
sourceList.add("Orange");// 使用集合 sourceList 初始化 ArrayList
ArrayList<String> targetList = new ArrayList<>(sourceList);// 输出 ArrayList 内容
System.out.println("ArrayList elements: " + targetList);
  • 使用匿名内部类来初始化
import java.util.ArrayList;public class ArrayListExample {public static void main(String[] args) {// 使用匿名内部类初始化 ArrayListArrayList<String> list = new ArrayList<String>() {{add("Apple");add("Banana");add("Orange");}};// 输出 ArrayList 内容System.out.println("ArrayList elements: " + list);}
}

这些是 ArrayList 的常见构造方式,可以根据具体的需求和场景选择合适的构造函数来创建 ArrayList 对象。

4. 增删改查

ArrayList 提供了一系列的方法来进行增删改查等功能操作:

  • 增加元素(Add):
  • add(E element): 向列表的末尾添加指定的元素。
  • add(int index, E element): 在列表的指定位置插入指定的元素。
  • addAll(Collection<? extends E> c): 将指定集合中的所有元素添加到列表的末尾。
  • addAll(int index, Collection<? extends E> c): 将指定集合中的所有元素插入到列表的指定位置。
  • 删除元素(Remove):
  • remove(Object o): 从列表中删除指定的元素,如果存在的话。
  • remove(int index): 删除列表中指定位置的元素。
  • removeAll(Collection<?> c): 删除列表中包含在指定集合中的所有元素。
  • clear(): 清空列表中的所有元素。
  • 修改元素(Modify):
  • set(int index, E element): 用指定的元素替换列表中指定位置的元素。
  • 查找元素(Retrieve):
  • get(int index): 返回列表中指定位置的元素。
  • indexOf(Object o): 返回列表中指定元素的第一个出现的索引,如果列表不包含此元素,则返回 -1。
  • lastIndexOf(Object o): 返回列表中指定元素的最后一个出现的索引,如果列表不包含此元素,则返回 -1。
  • 其他功能:
  • size(): 返回列表中的元素数。
  • isEmpty(): 如果列表不包含任何元素,则返回 true。
  • contains(Object o): 如果列表包含指定的元素,则返回 true。
  • toArray(): 返回包含列表中所有元素的数组。
    这些方法提供了对 ArrayList 中元素的常见操作。ArrayList 作为一个动态数组,支持在任意位置插入和删除元素,允许元素的重复,并且具有较好的随机访问性能。因此,ArrayList 在很多情况下是一个非常实用的数据结构。
    看例子
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class ArrayListExample {public static void main(String[] args) {// 创建一个空的 ArrayListArrayList<String> list = new ArrayList<>();// 添加元素list.add("Apple");list.add("Banana");list.add("Orange");System.out.println("After adding elements: " + list);// 在指定位置插入元素list.add(1, "Grapes");System.out.println("After inserting Grapes at index 1: " + list);// 删除指定元素list.remove("Banana");System.out.println("After removing Banana: " + list);// 删除指定位置的元素list.remove(0);System.out.println("After removing element at index 0: " + list);// 修改指定位置的元素list.set(0, "Strawberry");System.out.println("After setting element at index 0 to Strawberry: " + list);// 获取指定位置的元素String fruit = list.get(1);System.out.println("Element at index 1: " + fruit);// 检查是否包含某个元素boolean containsOrange = list.contains("Orange");System.out.println("Contains Orange: " + containsOrange);// 获取列表的大小int size = list.size();System.out.println("Size of the list: " + size);// 清空列表list.clear();System.out.println("After clearing the list: " + list);// 创建一个包含指定数组元素的 ListString[] array = {"Apple", "Banana", "Orange"};List<String> tempList = Arrays.asList(array);// 使用 ArrayList 构造函数从 List 初始化 ArrayListArrayList<String> arrayListFromList = new ArrayList<>(tempList);System.out.println("ArrayList from List: " + arrayListFromList);}
}

5.遍历

遍历 ArrayList 有几种常用的方法,包括使用迭代器、for-each 循环以及普通的 for 循环。下面是这些方法的示例:

  • 使用迭代器遍历 ArrayList:
import java.util.ArrayList;
import java.util.Iterator;public class ArrayListTraversal {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Orange");// 使用迭代器遍历 ArrayListIterator<String> iterator = list.iterator();while (iterator.hasNext()) {String element = iterator.next();System.out.println(element);}}
}
  • 使用 for-each 循环遍历 ArrayList:
import java.util.ArrayList;public class ArrayListTraversal {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Orange");// 使用 for-each 循环遍历 ArrayListfor (String element : list) {System.out.println(element);}}
}
  • 使用普通的 for 循环遍历 ArrayList:
import java.util.ArrayList;public class ArrayListTraversal {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Orange");// 使用普通的 for 循环遍历 ArrayListfor (int i = 0; i < list.size(); i++) {String element = list.get(i);System.out.println(element);}}
}

这些方法都可以用来遍历 ArrayList,你可以根据自己的喜好和代码风格选择合适的方法。通常情况下,使用 for-each 循环会更加简洁和直观,也可以配合stream进行操作。

小结

注意:ArrayList非线程安全使用时要注意,且只接受引用类型(可以存Integer但不能是int),这就意味着允许有null值。

slice

当谈到 Go 语言的切片(Slice)时,它是一个非常强大且常用的数据结构。切片提供了对序列的抽象,可以像数组一样访问元素,但它比数组更灵活、更强大。

1.数据结构

Go 语言中的切片(Slice)是一个动态长度的、灵活的数据结构,它是对数组的一个抽象。切片在底层的数据结构主要包括三个部分:

  • 指针(Pointer): 切片包含一个指向底层数组的指针,该指针指向数组中的第一个元素。通过指针,切片可以访问底层数组中的元素。

  • 长度(Length): 切片包含一个表示当前元素数量的长度字段。长度表示切片当前包含的元素数量,它不能超过底层数组的长度。

  • 容量(Capacity): 切片还包含一个表示可以容纳的最大元素数量的容量字段。容量表示切片从当前位置开始,到底层数组的末尾的元素数量。当向切片追加元素时,如果超过了切片的容量,Go 语言会自动扩展切片的容量,以保证足够的空间。

type slice struct {array unsafe.Pointer // 指向底层数组的指针len   int            // 切片当前长度cap   int            // 切片当前容量
}

在底层数组的基础上,切片提供了灵活的动态增长和缩减,这是因为切片可以通过修改其长度和容量字段来控制元素的数量。同时,由于切片是对底层数组的引用,因此对切片的操作也会影响到底层数组,反之亦然。

切片的底层数据结构使得它在内存中的表示比较轻量级,并且可以很方便地进行传递和复制。在实际开发中,切片是 Go 语言中常用的数据结构,用来处理动态大小的数据集合,例如字符串、字节数组等

2.重要特点

  • 动态大小: 切片是动态大小的,可以根据需要动态增长或缩小。与数组不同,切片的长度可以在运行时改变。

  • 引用数组: 切片是对底层数组的引用。在底层数组上进行操作会影响到切片,反之亦然。这意味着切片的创建和操作成本较低,因为它们不需要复制底层数组的数据。

  • 灵活的索引操作: 切片支持灵活的索引操作,可以使用切片表达式来获取子切片或修改切片的内容。

3.构造方式

  • 直接声明并初始化:

切片可以直接声明并初始化,使用 [] 表示切片,可以在 [] 中指定切片的长度和容量,也可以不指定。如果不指定长度和容量,切片的长度和容量都为 0。和数组不一样哦,数组至少还得三个dots[…]

// 声明一个切片并初始化
var slice1 []int // 声明一个空切片
slice2 := []int{1, 2, 3} // 声明并初始化一个切片
  • 通过数组进行切片:

可以通过数组来创建切片,通过指定数组的索引范围来获取子切片。

array := [5]int{1, 2, 3, 4, 5}
slice := array[1:3] // 从数组的索引 1 开始,到索引 3 结束,
  • 使用 make() 函数创建切片:

使用内置的 make() 函数来创建切片,该函数的语法为 make([]T, length, capacity),其中 T 表示切片的元素类型,length 表示切片的长度,capacity 表示切片的容量。

slice := make([]int, 3, 5) // 创建一个长度为 3,容量为 5 的切片

这里解释一下长度(len)和容量(cap)的问题
在 Go 语言中,len() 和 cap() 是两个用于切片(Slice)的内置函数,用于获取切片的长度和容量。

  • len() 函数:

len() 函数用于获取切片中当前包含的元素数量,即切片的长度。
切片的长度是切片中当前存储的元素数量。
无论切片的容量如何,len() 函数都只返回当前切片中实际存储的元素数量。

  • cap() 函数:

cap() 函数用于获取切片的容量,即切片的最大长度。
切片的容量是从切片的起始位置到底层数组末尾的元素数量。
切片的容量是底层数组中分配给切片的内存空间大小。
下面是一个示例来演示 len() 和 cap() 函数的用法及区别:

package mainimport "fmt"func main() {// 创建一个数组array := [5]int{1, 2, 3, 4, 5}// 对数组进行切片slice := array[1:3] // 从数组的索引 1 到索引 3(不包括索引 3)创建切片// 打印切片的长度和容量fmt.Println("Length of slice:", len(slice)) // 输出: 2fmt.Println("Capacity of slice:", cap(slice)) // 输出: 4// 打印切片的内容fmt.Println("Elements of slice:", slice) // 输出: [2 3]
}

在这个示例中,我们先创建了一个包含 5 个元素的数组。然后,我们从数组的索引 1 到索引 3(不包括索引 3)创建了一个切片。这个切片包含了数组的索引 1 和 2 处的元素。所以切片的长度是 2,容量是从索引 1 到数组末尾的元素数量,即 4。

4.增删改查

在 Go 语言中,切片(Slice)提供了丰富的操作方法,包括增加、删除、修改和查找元素等功能。下面是针对切片的常见操作示例:

  • 增加元素:

使用 append() 函数向切片中添加元素。

package mainimport "fmt"func main() {// 声明一个切片slice := []int{1, 2, 3}// 添加元素slice = append(slice, 4)fmt.Println("Slice after adding element:", slice) // 输出: [1 2 3 4]
}
  • 删除元素:

使用切片的切片操作或者 append() 函数配合切片的方式来删除元素。

package mainimport "fmt"func main() {// 声明一个切片slice := []int{1, 2, 3, 4, 5}// 删除第三个元素(索引为 2)slice = append(slice[:2], slice[3:]...)fmt.Println("Slice after deleting element:", slice) // 输出: [1 2 4 5]
}
  • 修改元素:

使用索引直接修改切片中的元素。

package mainimport "fmt"func main() {// 声明一个切片slice := []int{1, 2, 3}// 修改第二个元素(索引为 1)slice[1] = 5fmt.Println("Slice after modifying element:", slice) // 输出: [1 5 3]
}
  • 查找元素:

使用循环遍历切片并比较每个元素。

package mainimport "fmt"func main() {// 声明一个切片slice := []int{1, 2, 3, 4, 5}// 查找元素 3for i, v := range slice {if v == 3 {fmt.Println("Element 3 found at index:", i)break}}
}

这些示例演示了如何在 Go 中对切片进行增加、删除、修改和查找元素等操作。切片是 Go 语言中非常灵活和强大的数据结构,可以用来处理动态大小的数据集合。

5.遍历

这里跟数组一样,推荐使用for-range循环

package mainimport "fmt"func main() {// 声明一个切片slice := []int{1, 2, 3, 4, 5}// 使用 range 遍历切片for index, value := range slice {fmt.Printf("Index: %d, Value: %d\n", index, value)}
}

####总结
切片不是线程安全的数据结构,如果多个 Goroutine 同时操作同一个切片,可能会出现数据竞争等问题。
关于append()和copy()这两个函数,我后面会专门写一下。

总结

总的来说,Go和Java在设计可变数组时实现了相同的功能,不过细节上相差还是很大的。

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

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

相关文章

单片机FLASH深度解析和编程实践(下)

本篇文章将同大家分享单片机FLASH编程的相关寄存器和寄存器操作及库函数操作。本篇文章依然以STM32单片机为例进行解析。有关FLASH的基本原理和实现方法&#xff0c;大家可以参考上一篇文章&#xff1a;单片机FLASH深度解析和编程实践&#xff08;上&#xff09;-CSDN博客 目录…

物联网 3.15日 | 2024年中国七大 IoT 物联网云平台价格对比

随着 中国电信天翼 CTWing 物联网平台正式开始收费&#xff0c;国内物联网平台云产品发展进入成熟期&#xff0c;越来越多企业选择云厂商提供的物联网PaaS服务&#xff0c;以降低运营成本&#xff0c;缩短业务上线周期&#xff0c;释放运维的人力&#xff0c;按需付费动态扩容。…

【STM32定时器 TIM小总结】

STM32 TIM详解 TIM介绍定时器类型基本定时器通用定时器高级定时器常用名词时序图预分频时序计数器时序图 定时器中断配置图定时器定时 代码调试 TIM介绍 定时器&#xff08;Timer&#xff09;是微控制器中的一个重要模块&#xff0c;用于生成定时和延时信号&#xff0c;以及处…

宝塔 安装对外服务Tomcat和JDK

一、安装Tomcat\JDK 切记1&#xff1a;如果选择下载节点失败&#xff0c;请到软件商城安装 。 切记2&#xff1a;提醒安装Nginx或Apache &#xff0c;先点安装&#xff0c;进入再打叉关闭。因为Tomcat服务足够为我们搭建JavaWeb网站服务了。 切记3&#xff1a;Nginx占用80端口…

macOS 安装 NetLogo 6.4.0

netlogo 下载地址 NetLogo-6.4.0.dmg参考 netlogo 官网

17.搜索二维矩阵Ⅱ

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,2…

Windows 安装配置 RabbitMQ 详解

目录 1、安装前准备2、安装Erlang2.1 安装2.2 配置环境变量 3、安装RabbitMQ3.1 安装3.2 配置环境变量3.3 安装rabbitmq_management插件3.4 启动RabbitMQ服务 4、常用命令 本文将详说如何在Windows系统中安装RabbitMQ。 1、安装前准备 因为RabbitMQ服务器是用Erlang语言编写的…

0G联合创始人MICHAEL HEINRICH确认出席Hack.Summit() 2024区块链开发者大会

随着区块链技术的不断发展和应用&#xff0c;全球开发者瞩目的Hack.Summit() 2024区块链开发者大会即将于2024年4月9日至10日在香港数码港盛大举行。此次大会由Hack VC主办&#xff0c;并得到AltLayer和Berachain的协办&#xff0c;同时汇聚了Solana、The Graph、Blockchain Ac…

论文阅读——Rein

Stronger, Fewer, & Superior: Harnessing Vision Foundation Models for Domain Generalized Semantic Segmentation 一、引言 是一个对Domain Generalized Semantic Segmentation (DGSS)任务的视觉大模型的微调方法&#xff0c;即Rein。 Rein 专为 DGSS 任务量身定制&a…

Segment Routing IPv6简介

定义 SRv6&#xff08;Segment Routing IPv6&#xff0c;基于IPv6转发平面的段路由&#xff09;是基于源路由理念而设计的在网络上转发IPv6数据包的一种协议。SRv6通过在IPv6报文中插入一个路由扩展头SRH&#xff08;Segment Routing Header&#xff09;&#xff0c;在SRH中压…

Tomcat Session 集群 ---------会话保持

一、 负载均衡、反向代理 环境搭建&#xff1a; nginx服务器192.168.246.7 tomcat 1服务器192.168.246.8 tomcat 2服务器192.168.246.9 7-1 nginx服务器搭建 [rootzzcentos1 ~]#systemctl stop firewalld [rootzzcentos1 ~]#setenforce 0 [rootzzcentos1 ~]#yum install …

计算机网络——物理层(编码与调制)

计算机网络——编码与调制 基带信号和宽带信号编码与调制数字数据编码为数字信号非归零编码归零编码反向不归零编码曼彻斯特编码差分曼彻斯特编码4B/5B编码 数字数据调制为模拟信号模拟数据编码为数字信号模拟数据调制为模拟信号 我们之前讲了物理层的一些基础知识和两个准则&a…

4.MongoDB中16个常用CURD

基本的CURD 作为一个非专业的DBA&#xff0c;我们只需要会一些基本的curd就行&#xff0c;专业的内容还是需要专业的人去干的。CRUD 也就是增删改查&#xff0c;这是数据库最基本的功能&#xff0c;查询还支持全文检索&#xff0c;GEO 地理位置查询等。 01创建库 无需单独创…

以题为例浅谈文件包含

什么叫做文件包含 文件包含函数加载的参数没有经过过滤或严格定义&#xff0c;可以被用户控制&#xff0c; 包含其他恶意文件&#xff0c;导致了执行非预期代码。 文件包含漏洞&#xff08;File Inclusion Vulnerability&#xff09;是一种常见的网络安全漏洞&#xff0c;它允…

Linux中 vim 编辑器的使用

文章目录 前言一、vim编辑器模式二、简单的插入、保存和退出三、 命令模式下常用命令即其作用1. 命令模式 思维导图 前言 首先&#xff0c;了解一下 什么是vim 编辑器&#xff1f;在不同的系统中&#xff0c;文本的管理也会不同&#xff1b;windos系统就不多说了&#xff0c…

unity内存优化之AB包篇(微信小游戏)

1.搭建资源服务器使用(HFS软件(https://www.pianshen.com/article/54621708008/)) using System.Collections; using System.Collections.Generic; using UnityEngine;using System;public class Singleton<T> where T : class, new() {private static readonly Lazy<…

idea将非UTF-8的properties修改为UTF-8编码的文件

需求背景 由于项目初始化时&#xff0c;properties文件的编码格式为ASCII编码格式&#xff0c;此时用idea打开该文件会默认展示UTF-8的编码内容&#xff0c;其中汉字可以正常展示&#xff0c;但是使用notepad打开却依旧时ASCII编码格式 idea配置 打开idea-setting-editor-f…

力扣112、113、101--树

112. 路径总和 题目描述&#xff1a; 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。 判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。 如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c…

hcia复习总结7

1&#xff0c;AR2发送2.0网段的信息给AR1&#xff0c;如果&#xff0c;AR1本身并不存在该网段的路由 信息&#xff0c;则将直接 刷新 到本地的路由表中。 Destination/Mask Proto Pre Cost Flags NextHop Interface 2.2.2.0/24 RIP 100…

多媒体会议系统的优势与核心组成

随着科技的发展&#xff0c;多媒体会议系统已经成为现代商务沟通的重要工具。这种集成了多种通信和信息技术的系统&#xff0c;旨在提高会议的效率和参与度&#xff0c;具有诸多优势。本文将对多媒体会议系统进行详细的介绍和分析&#xff0c;并探讨其对现代商务沟通的影响。 …