深入理解 Java 数组-程序员宅基地

技术标签: java  数据结构与算法  c/c++  

:notebook: 本文已归档到:「blog

:keyboard: 本文中的示例代码已归档到:「javacore

简介

数组的特性

数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。几乎所有程序设计语言都支持数组。

数组代表一系列对象或者基本数据类型,所有相同的类型都封装到一起,采用一个统一的标识符名称。

数组的定义和使用需要通过方括号 []

Java 中,数组是一种引用类型。

Java 中,数组是用来存储固定大小的同类型元素。

数组和容器

Java 中,既然有了强大的容器,是不是就不需要数组了?

答案是不。

诚然,大多数情况下,应该选择容器存储数据。

但是,数组也不是毫无是处:

  • Java 中,数组是一种效率最高的存储和随机访问对象引用序列的方式。数组的效率要高于容器(如 ArrayList)。
  • 数组可以持有值类型,而容器则不能(这时,就必须用到包装类)。

Java 数组的本质是对象

Java 数组的本质是对象。它具有 Java 中其他对象的一些基本特点:封装了一些数据,可以访问属性,也可以调用方法。所以,数组是对象。

如果有两个类 A 和 B,如果 B 继承(extends)了 A,那么 A[] 类型的引用就可以指向 B[] 类型的对象。

扩展阅读:Java 中数组的特性

如果想要论证 Java 数组本质是对象,不妨一读这篇文章。

Java 数组和内存

Java 数组在内存中的存储是这样的:

数组对象(这里可以看成一个指针)存储在栈中。

数组元素存储在堆中。

如下图所示:只有当 JVM 执行 new String[] 时,才会在堆中开辟相应的内存区域。数组对象 array 可以视为一个指针,指向这块内存的存储地址。

声明数组

声明数组变量的语法如下:

int[] arr1; // 推荐风格
int arr2[]; // 效果相同
复制代码

创建数组

Java 语言使用 new 操作符来创建数组。有两种创建数组方式:

  • 指定数组维度
    • 为数组开辟指定大小的数组维度。
    • 如果数组元素是基础数据类型,会将每个元素设为默认值;如果是引用类型,元素值为 null
  • 不指定数组维度
    • 用花括号中的实际元素初始化数组,数组大小与元素数相同。

示例 1:

public class ArrayDemo {
    public static void main(String[] args) {
        int[] array1 = new int[2]; // 指定数组维度
        int[] array2 = new int[] { 1, 2 }; // 不指定数组维度

        System.out.println("array1 size is " + array1.length);
        for (int item : array1) {
            System.out.println(item);
        }

        System.out.println("array2 size is " + array1.length);
        for (int item : array2) {
            System.out.println(item);
        }
    }
}
// Output:
// array1 size is 2
// 0
// 0
// array2 size is 2
// 1
// 2
复制代码

:bulb: 说明 请注意数组 array1 中的元素虽然没有初始化,但是 length 和指定的数组维度是一样的。这表明指定数组维度后,无论后面是否初始化数组中的元素,数组都已经开辟了相应的内存

数组 array1 中的元素都被设为默认值。

示例 2:

public class ArrayDemo2 {
    static class User {}

    public static void main(String[] args) {
        User[] array1 = new User[2]; // 指定数组维度
        User[] array2 = new User[] {
    new User(), new User()}; // 不指定数组维度

        System.out.println("array1: ");
        for (User item : array1) {
            System.out.println(item);
        }

        System.out.println("array2: ");
        for (User item : array2) {
            System.out.println(item);
        }
    }
}
// Output:
// array1:
// null
// null
// array2:
// io.github.dunwu.javacore.array.ArrayDemo2$User@4141d797
// io.github.dunwu.javacore.array.ArrayDemo2$User@68f7aae2
复制代码

:bulb: 说明

请将本例与示例 1 比较,可以发现:如果使用指定数组维度方式创建数组,且数组元素为引用类型,则数组中的元素元素值为 null

数组维度的形式

创建数组时,指定的数组维度可以有多种形式:

  • 数组维度可以是整数、字符。
  • 数组维度可以是整数型、字符型变量。
  • 数组维度可以是计算结果为整数或字符的表达式。

示例:

public class ArrayDemo3 {
    public static void main(String[] args) {
        int length = 3;
        // 放开被注掉的代码,编译器会报错
        // int[] array = new int[4.0];
        // int[] array2 = new int["test"];
        int[] array3 = new int['a'];
        int[] array4 = new int[length];
        int[] array5 = new int[length + 2];
        int[] array6 = new int['a' + 2];
        // int[] array7 = new int[length + 2.1];
        System.out.println("array3.length = [" + array3.length + "]");
        System.out.println("array4.length = [" + array4.length + "]");
        System.out.println("array5.length = [" + array5.length + "]");
        System.out.println("array6.length = [" + array6.length + "]");
    }
}
// Output:
// array3.length = [97]
// array4.length = [3]
// array5.length = [5]
// array6.length = [99]
复制代码

? 说明

当指定的数组维度是字符时,Java 会将其转为整数。如字符 a 的 ASCII 码是 97。

综上,Java 数组的数组维度可以是常量、变量、表达式,只要转换为整数即可

请留意,有些编程语言则不支持这点,如 C/C++ 语言,只允许数组维度是常量。

数组维度的大小

数组维度并非没有上限的,如果数值过大,编译时会报错。

int[] array = new int[6553612431]; // 数组维度过大,编译报错
复制代码

此外,数组过大,可能会导致栈溢出

访问数组

Java 中,可以通过在 [] 中指定下标,访问数组元素,下标位置从 0 开始。

public class ArrayDemo4 {
    public static void main(String[] args) {
        int[] array = {
    1, 2, 3};
        for (int i = 0; i < array.length; i++) {
            array[i]++;
            System.out.println(String.format("array[%d] = %d", i, array[i]));
        }
    }
}
// Output:
// array[0] = 2
// array[1] = 3
// array[2] = 4
复制代码

? 说明

上面的示例中,从 0 开始,使用下标遍历数组 array 的所有元素,为每个元素值加 1 。

数组的引用

Java 中,数组类型是一种引用类型

因此,它可以作为引用,被 Java 函数作为函数入参或返回值

数组作为函数入参的示例:

public class ArrayRefDemo {
    private static void fun(int[] array) {
        for (int i : array) {
            System.out.print(i + "\t");
        }
    }

    public static void main(String[] args) {
        int[] array = new int[] {
    1, 3, 5};
        fun(array);
    }
}
// Output:
// 1	3	5
复制代码

数组作为函数返回值的示例:

public class ArrayRefDemo2 {
    /**
     * 返回一个数组
     */
    private static int[] fun() {
        return new int[] {
    1, 3, 5};
    }

    public static void main(String[] args) {
        int[] array = fun();
        System.out.println(Arrays.toString(array));
    }
}
// Output:
// [1, 3, 5]
复制代码

泛型和数组

通常,数组和泛型不能很好地结合。你不能实例化具有参数化类型的数组。

Peel<Banana>[] peels = new Pell<Banana>[10]; // 这行代码非法
复制代码

Java 中不允许直接创建泛型数组。但是,可以通过创建一个类型擦除的数组,然后转型的方式来创建泛型数组。

public class GenericArrayDemo<T> {

    static class GenericArray<T> {
        private T[] array;

        public GenericArray(int num) {
            array = (T[]) new Object[num];
        }

        public void put(int index, T item) {
            array[index] = item;
        }

        public T get(int index) { return array[index]; }

        public T[] array() { return array; }
    }



    public static void main(String[] args) {
        GenericArray<Integer> genericArray = new GenericArray<Integer>(4);
        genericArray.put(0, 0);
        genericArray.put(1, 1);
        Object[] array = genericArray.array();
        System.out.println(Arrays.deepToString(array));
    }
}
// Output:
// [0, 1, null, null]
复制代码

扩展阅读:www.cnblogs.com/jiangzhaowe…

我认为,对于泛型数组的理解,点到为止即可。实际上,真的需要存储泛型,还是使用容器更合适。

多维数组

多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。

Java 可以支持二维数组、三维数组、四维数组、五维数组。。。

但是,以正常人的理解能力,一般也就最多能理解三维数组。所以,请不要做反人类的事,去定义过多维度的数组。

多维数组使用示例:

public class MultiArrayDemo {
    public static void main(String[] args) {
        Integer[][] a1 = { // 自动装箱
            {
    1, 2, 3,},
            {
    4, 5, 6,},
        };
        Double[][][] a2 = { // 自动装箱
            { {
    1.1, 2.2}, {
    3.3, 4.4} },
            { {
    5.5, 6.6}, {
    7.7, 8.8} },
            { {
    9.9, 1.2}, {
    2.3, 3.4} },
        };
        String[][] a3 = {
            {
    "The", "Quick", "Sly", "Fox"},
            {
    "Jumped", "Over"},
            {
    "The", "Lazy", "Brown", "Dog", "and", "friend"},
        };
        System.out.println("a1: " + Arrays.deepToString(a1));
        System.out.println("a2: " + Arrays.deepToString(a2));
        System.out.println("a3: " + Arrays.deepToString(a3));
    }
}
// Output:
// a1: [[1, 2, 3], [4, 5, 6]]
// a2: [[[1.1, 2.2], [3.3, 4.4]], [[5.5, 6.6], [7.7, 8.8]], [[9.9, 1.2], [2.3, 3.4]]]
// a3: [[The, Quick, Sly, Fox], [Jumped, Over], [The, Lazy, Brown, Dog, and, friend]]
复制代码

Arrays 类

Java 中,提供了一个很有用的数组工具类:Arrays。

它提供的主要操作有:

  • sort - 排序
  • binarySearch - 查找
  • equals - 比较
  • fill - 填充
  • asList - 转列表
  • hash - 哈希
  • toString - 转字符串

扩展阅读:juejin.im/post/5a6ade…

小结



参考资料

转载于:https://juejin.im/post/5c88e75fe51d4520a700b9f3

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33863087/article/details/91464273

智能推荐

com.alibaba.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer.-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏3次。com.alibaba.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer.超时异常:等待服务响应超时。出现这个问题的分析:1,jar包依赖问题,没有成功引入依赖。2,服务提供方打了断点,导致代码执行堵塞,响应超时。3,服务端后台代码报错导致服务没有被成功调用(..._com.alibaba.dubbo.remoting.timeoutexception: waiting server-side response ti

利用Gh0st 3.6远程溢出漏洞反向控制攻击者_gh0st漏洞-程序员宅基地

文章浏览阅读883次。title: 利用Gh0st 3.6远程溢出漏洞反向控制攻击者comments: truetoc: truecategories:[Metasploit][Exp]tags:MetasploitOverflowGh0stdate: 2020-01-12 18:30:10abbrlink: 30568前言漏洞验证在2017年被公开,实际上Gh0st溢出漏洞在2009..._gh0st漏洞

EffectiveC++-条款25:考虑写出一个不抛异常的 swap 函数_effective c++ 条款25-程序员宅基地

文章浏览阅读206次。## 一. 内容1. swap 是一个有趣的函数。原本它只是STL的一部分,后面成为异常安全性编程的重要支柱(见条款29),以及处理自我赋值的常见机制(见条款11)。swap如此有用,考虑其合理的实现变得十分重要。2. 所谓 swap(置换),就是将两个对象的值彼此赋值给对方。3. 默认情况下,swap 可以交给 标准程序库提供的 swap 算法完成,其典型实现正如预期。 ```cpp namespace std{ template void swap(............_effective c++ 条款25

脚本和zabbix监控_不依赖zabbix,脚本监控-程序员宅基地

文章浏览阅读2.5k次,点赞3次,收藏9次。1 .LNMP环境一键安装脚本1.1 要求可编译也可yum安装,最终显示phpinfo信息:修改nginx默认端口为8000修改nginx的连接数为10240修改nginx的默认首页启动每个服务前,需要先检测服务是否存在1.2 脚本内容如下:#!/bin/bashnginx_install() { if [ -f /root/nginx-1.18.0.tar.gz ];then echo "nginx源码包存在,开始解压..." cd /root_不依赖zabbix,脚本监控

MybatisPlus实现多数据源_mybatis plus多数据源 @primary-程序员宅基地

文章浏览阅读2.2k次。关于mybatisPlus多数据源的一点小小的记录_mybatis plus多数据源 @primary

腾讯云颜松柏:详解DevOps成熟度模型与效能度量-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏17次。成熟度模型是一次性评估,效能度量是持续、定期的度量_devops成熟度模型

随便推点

Surface实现TF卡槽引导Ventoy加载Fydeos,不格式化硬盘,不重装Windows!_ventoy scanning files,please wait-程序员宅基地

文章浏览阅读7.1k次,点赞4次,收藏6次。不格式化原有的Windows系统盘,用TF卡引导Fydeos启动!_ventoy scanning files,please wait

matlab中comet_matlab中comet的语法规则-程序员宅基地

文章浏览阅读996次。先运行edit comet然后在comet文件中所有for…end循环中加上一句‘pause(0.1);’ps:pause(0.1);表示暂停0.1秒。_matlab中comet的语法规则

【C语言】枚举(enum)_c语言枚举-程序员宅基地

文章浏览阅读2.4k次,点赞4次,收藏21次。一、枚举语法的定义枚举(enum)是一种数据类型,它的语法定义格式为:enum枚举名{枚举元素1,枚举元素2,......};例如,一个星期有七天,使用枚举法进行定义为:enum Day{ Mon,Tue,Wed,Thu,Fri,Sat,Sun};需要注意的是,第一个枚举元素的默认值为0,后面的枚举元素的值在上一枚举元素的值上加1。因此,Mon=0,Tue=1,Wed=2,Thu=3,Fri=4,Sat=5,Sun=6.但是,在定义枚举类型时,可以改..._c语言枚举

2023 年江西省职业院校技能大赛 智能制造设备技术应用赛项任务书——学生赛样题_机器人拾取吸盘工具到物料区拾取物料并判断将合格品放到原料区不合格品放到回-程序员宅基地

文章浏览阅读982次,点赞20次,收藏21次。产品所在工位推动气缸缩回,缩回到位后升降气缸下降,下降到 位后检测LED灯闪烁,4s后,升降气缸上升,上升到位后推动气缸伸出,结果指示灯点亮(当检测结果为优良品时,绿灯常亮,当检测结果为废品时,红灯常亮,当检测为合格品时,红色和绿色指示灯交替闪烁)。1.程序正常运行过程中,若触发安全光栅,工业机器人速度降至当前速度的50%,降速超过6s,工业机器人停止运行。完成后,机器人回到安全点,暂停。4.按下 “开始”按钮,根据初次检测结果完成产品的入库工作,优良品的产品放入成品区,完成后机器人放下工具,回安全点。_机器人拾取吸盘工具到物料区拾取物料并判断将合格品放到原料区不合格品放到回

时间序列数据库KDB 与Java结合使用介绍 -- 3 基于KDB JDBC的写入实现_kdb支持sql吗-程序员宅基地

文章浏览阅读1.6k次。时间序列数据库KDB 与Java结合使用介绍 -- 3 基于KDB JDBC的写入实现_kdb支持sql吗

rgb颜色处理_rgb处理-程序员宅基地

这篇文章讲述了RGB颜色处理的方法,包括使用numpy和matplotlib库来处理图像,使用LAB色彩空间进行处理。

推荐文章

热门文章

相关标签