前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你真的了解 Java 数组?

你真的了解 Java 数组?

原创
作者头像
Lorin 洛林
发布2023-11-13 18:03:44
1620
发布2023-11-13 18:03:44
举报
文章被收录于专栏:Java 技术小屋

前言

  • 大家好,我是 Lorin,数组是我们日常开发中使用非常广泛的一种数据结构,但是大家真的已经了解它了?我们最大可以开辟多大的数组?数组底层如何存储?什么时候我们应该选择数组?接下来我和大家一起来一一解决这些问题。
  • 本文源码版本:JDK 8

如何创建一个数组

静态初始化

  • 在声明数组时就为其分配和初始化元素的值。
代码语言:java
复制
int[] arr = {1, 2, 3, 4, 5}; // 初始化一个整数数组
String[] arr = {"Alice", "Bob", "Charlie"}; // 初始化一个字符串数组

Java 5及更高版本支持数组初始化表达式,允许你在创建数组时同时为其赋初值:
int[] arr = new int[] {1, 2, 3, 4, 5}; // 使用数组初始化表达式

动态初始化

  • 在声明数组后,通过循环或其他逻辑逐个分配元素的值。
代码语言:java
复制
int[] arr = new int[5]; // 创建一个包含5个整数的数组,初始值为0

// for 循环赋值
for (int i = 0; i < arr.length; i++) {
    arr[i] = i + 1; // 为数组元素赋值
}

// 使用Arrays类的方法初始化
Arrays.fill(arr, 7); // 填充整个数组

常见问题

数组大小不够如何处理

  • 普通数组无法支持动态拓展,一般有以下三个方案:

优化代码

  • 在一些情况下,可以通过更有效的方式来处理大量数据而无需增加数组大小。

手动扩展

  • 如果你使用的是普通数组,你可以手动创建一个更大的数组,将数据从旧数组复制到新数组,然后使用新数组。这需要更多的手动管理,但可以有效解决数组大小不足的问题。
代码语言:Java
复制
int[] oldArray = new int[10];
int[] newArray = new int[20];
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
// 更新引用
oldArray = newArray;

使用集合

  • 如果你需要处理可变大小的数据集合,可以考虑使用Java集合框架中的数据结构,如ArrayList、LinkedList、HashSet等,这些数据结构支持动态拓展。

数组的默认值

  • 当你创建一个普通数组并且没有显式初始化它的元素时,所有元素将被自动初始化为相应数据类型的默认值。例如:
代码语言:java
复制
int[] intArray = new int[5]; // 所有元素初始化为0
char[] charArray = new char[3]; // 所有元素初始化为'\u0000'
boolean[] booleanArray = new boolean[4]; // 所有元素初始化为false

int 数组的默认值是0
double 数组的默认值是0.0
float 数组的默认值是0.0f
char 数组的默认值是'\u0000',即空字符
byte 数组的默认值是0
short 数组的默认值是0
long 数组的默认值是0L
boolean 数组的默认值是false
对象数组(数组元素为引用类型),默认值是null

最大可用数组影响因素

数组索引长度限制

  • 在Java中,数组的长度是由int类型的索引来表示的,因此数组的最大长度受到int类型的范围限制。因此,数组的最大长度可以达到Integer.MAX_VALUE,它的值是 2,147,483,647。
代码语言:java
复制
int[] arr = new int[Integer.MAX_VALUE + 1];

Exception in thread "main" java.lang.NegativeArraySizeException
	at com.example.springboottestmaven.Test.main(Test.java:44)

堆内存限制

  • 普通数组存储在堆内存中,因此受到堆内存限制。
代码语言:java
复制
int[] arr = new int[Integer.MAX_VALUE - 1];

Exception in thread "main" java.lang.NegativeArraySizeException
	at com.example.springboottestmaven.Test.main(Test.java:44)
	
Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	at com.example.springboottestmaven.Test.main(Test.java:44)

底层存储方式

  • 数组的底层存储是连续的内存块,每个元素占用固定大小的内存。具体的存储方式取决于元素的类型,因为 Java 数组支持不同类型的元素,包括基本数据类型和引用类型。
  • 在 Java 实现中,栈存储了数组的引用,实际数据存储在堆中,根据存储数据类型不同,存在一些差异,具体差异见下文。

基本数据类型数组

  • 对于基本数据类型(如int、char、byte等)的数组,每个元素的值直接存储在连续的内存位置上。这些数据类型的大小是已知的,因此可以通过简单的偏移量来访问每个元素。
  • 例如,对于int数组,每个int元素占用4个字节(32位),可以通过元素的索引和每个元素的大小来计算偏移量,以快速访问数组中的元素。

引用类型数组

  • 对于引用类型(对象类型)的数组,数组存储的是对象引用,而实际的对象存储在堆内存中。每个元素的大小是引用的大小,通常是4字节或8字节,具体取决于系统的位数和JVM配置。

多维数组

  • 多维数组的存储方式是数组的数组,它们的元素也是连续存储的,但每个元素可以是另一个数组,从而构成多维数组。多维数组的存储方式类似于矩阵,每个行数组存储在连续内存中,并且各行之间也是连续排列的。

多维数组行内存连续,行与行之间的内存连续?

  • 二维数组的行通常是连续存储的,但不同行之间的内存不一定连续。这意味着每个行数组的元素在内存中是紧密排列的,但各行之间可能存在间隙。
代码语言:txt
复制
// 具体的内存布局可能会受到Java虚拟机的实现和底层硬件的影响,因此不同的系统和编译器可能会有不同的细节。
  +---+---+---+    +---+---+---+    +---+---+---+
  | 1 | 2 | 3 |    | 4 | 5 | 6 |    | 7 | 8 | 9 |
  +---+---+---+    +---+---+---+    +---+---+---+
    Row 1            Row 2            Row 3
代码语言:txt
复制
# Tips
Java数组在内存中的存储是由Java虚拟机(JVM)和底层硬件来管理的,开发者不直接操作内存地址。
因此,数组的底层存储方式可能在不同的JVM实现和硬件平台上有所不同.
上述说明提供了一种一般性的概念,实际实现可能有细微差异。

优缺点

优点

快速随机访问

  • 数组中的元素可以通过索引快速访问,具有O(1)的时间复杂度。这使得数组在需要快速查找或访问元素时非常有用。

内存连续性

  • 数组的元素在内存中是连续存储的,这有助于提高缓存性能,因为现代计算机系统倾向于预读连续的内存块。

缺点

大小固定,不支持动态拓展

  • 数组的大小在创建时就被确定,难以动态扩展。如果需要更多空间,通常需要创建一个新的数组,将数据复制到新数组中,然后释放旧数组。

插入和删除低效

  • 在数组中插入或删除元素通常需要大量的数据迁移,因为需要保持元素的连续性。这可能导致性能问题。

最佳实践

注意适宜场景

  • 数组底层是连续存储的,对于随机存取具有O(1)的时间复杂度,适合连续存储的数据,但对于需要插入、删除效率较低。

优先考虑集合

  • 在大多数情况下,使用集合类(如 ArrayList 底层就是数组,, LinkedList, HashSet)而不是数组可以更方便地管理数据,因为它们具有自动大小调整、插入和删除操作效率更高等优势。只有在需要特定的性能、内存或数据结构特性时,才使用数组。
  • 如 ArrayList 底层实现是数组,但是基于数组实现了更多的功能,比如动态扩容等。

合适的数组大小

  • 如果你知道数组的大小,尽量在创建数组时指定初始大小,以避免后续的数组大小调整操作。或申请较多的内存导致内存浪费。

注意边界检查

  • 确保在访问数组元素时进行足够的边界检查,以避免数组索引越界异常。

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 如何创建一个数组
    • 静态初始化
      • 动态初始化
      • 常见问题
        • 数组大小不够如何处理
          • 优化代码
        • 手动扩展
          • 使用集合
          • 数组的默认值
          • 最大可用数组影响因素
            • 数组索引长度限制
              • 堆内存限制
              • 底层存储方式
                • 基本数据类型数组
                  • 引用类型数组
                    • 多维数组
                      • 多维数组行内存连续,行与行之间的内存连续?
                  • 优缺点
                    • 优点
                      • 快速随机访问
                      • 内存连续性
                    • 缺点
                      • 大小固定,不支持动态拓展
                      • 插入和删除低效
                  • 最佳实践
                    • 注意适宜场景
                      • 优先考虑集合
                        • 合适的数组大小
                          • 注意边界检查
                          • 个人简介
                          相关产品与服务
                          对象存储
                          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档