两年前,我甚至写过一篇文章,吐槽数组在 Java 中挺鸡肋的,因为有 List 谁用数组啊,现在想想那时候的自己好幼稚,好可笑。因为我只看到了表面现象,实际上呢,List 的内部仍然是通过数组实现的,比如说 ArrayList,在它的源码里可以看到下面这些内容:

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * will be expanded to DEFAULT_CAPACITY when the first element is added.
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * The size of the ArrayList (the number of elements it contains).
 *
 * @serial
 */
private int size;

数组在 Java 中,必须算是核心,神一般的存在。

01、什么是数组

按照 Javadoc 给出的解释,数组是一个对象,它包含了一组固定数量的元素,并且这些元素的类型是相同的。数组会按照索引的方式将元素放在指定的位置上,意味着我们可以通过索引来访问到这些元素。在 Java 中,索引是从 0 开始的。

我们可以将数组理解为一个个整齐排列的单元格,每个单元格里面存放着一个元素。

数组元素的类型可以是基本数据类型(比如说 int、double),也可以是引用数据类型(比如说 String),包括自定义类型的对象。

了解了数组的定义后,让我们来深入地研究一下数组的用法。

在 Java 中,数组的声明方式有两种。

先来看第一种:

int[] anArray;

再来看第二种:

int anOtherArray[];

不同之处就在于中括号的位置,是紧跟类型,还是放在变量名的后面。前者比后者的使用频率更高一些。

接下来就该看看怎么初始化数组了,同样有多种方式可以初始化数组,比如说最常见的是:

int[] anArray = new int[10];

使用了 new 关键字,对吧?这就意味着数组的确是一个对象。然后,在方括号中指定了数组的长度,这是必须的。

这时候,数组中的每个元素都会被初始化为默认值,int 类型的就为 0,Object 类型的就为 null。

另外,还可以使用大括号的方式,直接初始化数组中的元素:

int anOtherArray[] = new int[] {1, 2, 3, 4, 5};

这时候,数组的元素分别是 1、2、3、4、5,索引依次是 0、1、2、3、4。

02、访问数组

前面提到过,可以通过索引来访问数组的元素,就像下面这样:

anArray[0] = 10;
System.out.println(anArray[0]);

通过数组的变量名,加上中括号,加上元素的索引,就可以访问到数组,通过“=”操作符进行赋值。

如果索引的值超出了数组的界限,就会抛出 ArrayIndexOutOfBoundException,关于这方面的知识,我之前特意写过一篇文章,如果你感兴趣的话,可以跳转过去看看。

为什么会发生ArrayIndexOutOfBoundsException

我觉得原因挺有意思的。

既然数组的索引是从 0 开始,那就是到数组的 length - 1 结束,不要使用超出这个范围内的索引访问数组,就不会抛出数组越界的异常了。

03、遍历数组

当数组的元素非常多的时候,逐个访问数组就太辛苦了,所以需要通过遍历的方式。

第一种,使用 for 循环:

int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
for (int i = 0; i < anOtherArray.length; i++) {
    System.out.println(anOtherArray[i]);
}

通过 length 属性获取到数组的长度,然后索引从 0 开始遍历,就得到了数组的所有元素。

第二种,使用 for-each 循环:

for (int element : anOtherArray) {
    System.out.println(element);
}

如果不需要关心索引的话(意味着不需要修改数组的某个元素),使用 for-each 遍历更简洁一些。当然,也可以使用 while 和 do-while 循环。

04、可变参数

可变参数用于将任意数量的参数传递给方法:

void varargsMethod(String... varargs) {}

varargsMethod() 方法可以传递任意数量的字符串参数,可以是 0 个或者 N 个,本质上,可变参数就是通过数组实现的,为了证明这一点,我们可以通过 jad 反编译一下字节码:

public class VarargsDemo
{

    public VarargsDemo()
    {
    }

    transient void varargsMethod(String as[])
    {
    }
}

所以我们其实可以直接将数组作为参数传递给可变参数的方法:

VarargsDemo demo = new VarargsDemo();
String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员"};
demo.varargsMethod(anArray);

也可以直接传递多个字符串,通过逗号隔开的方式:

demo.varargsMethod("沉默王二", "一枚有趣的程序员");

05、把数组转成 List

List 封装了很多常用的方法,方便我们对集合进行一些操作,而如果直接操作数组的话,多有不便,因此有时候我们需要把数组转成 List。

最原始的方式,就是通过遍历数组的方式,一个个将数组添加到 List 中。

int[] anArray = new int[] {1, 2, 3, 4, 5};

List<Integer> aList = new ArrayList<>();
for (int element : anArray) {
    aList.add(element);
}

更优雅的方式是通过 Arrays 类的 asList() 方法:

List<Integer> aList = Arrays.asList(anArray);

但需要注意的是,该方法返回的 ArrayList 并不是 java.util.ArrayList,它其实是 Arrays 类的一个内部类:

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable{}

如果需要添加元素或者删除元素的话,最好把它转成 java.util.ArrayList

new ArrayList<>(Arrays.asList(anArray));

06、把数组转成 Stream

Java 8 新增了 Stream 流的概念,这就意味着我们也可以将数组转成 Stream 进行操作,而不是 List。

String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员", "好好珍重他"};
Stream<String> aStream = Arrays.stream(anArray);

也可以直接对数组的元素进行剪辑,通过指定索引的方式:

Stream<String> anotherStream = Arrays.stream(anArray, 1, 3);

结果包含"一枚有趣的程序员"和"好好珍重他",1 这个索引位置包括,3 这个索引位置不包括。

07、数组排序

Arrays 类提供了一个 sort() 方法,可以对数组进行排序。

  • 基本数据类型按照升序排列
  • 实现了 Comparable 接口的对象按照 compareTo() 的排序

来看第一个例子:

int[] anArray = new int[] {5, 2, 1, 4, 8};
Arrays.sort(anArray);

排序后的结果如下所示:

[1, 2, 4, 5, 8]

来看第二个例子:

String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"};
Arrays.sort(yetAnotherArray, 1, 3,
                Comparator.comparing(String::toString).reversed());

只对 1-3 位置上的元素进行反序,所以结果如下所示:

[A, Z, E, B, C]

08、数组搜索

有时候,我们需要从数组中查找某个具体的元素,最直接的方式就是通过遍历的方式:

int[] anArray = new int[] {5, 2, 1, 4, 8};
for (int i = 0; i < anArray.length; i++) {
    if (anArray[i] == 4) {
        System.out.println("找到了 " + i);
        break;
    }
}

上例中从数组中查询元素 4,找到后通过 break 关键字退出循环。

如果数组提前进行了排序,就可以使用二分查找法,这样效率就会更高一些。Arrays.binarySearch() 方法可供我们使用,它需要传递一个数组,和要查找的元素。

int[] anArray = new int[] {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(anArray, 4);

09、总结

除了一维数组,还有二维数组,但说实话,二维数组不太常用,这里就不再介绍了,感兴趣的话,可以尝试打印以下杨辉三角。

这篇文章,我们介绍了 Java 数组的基本用法和一些高级用法,我想小伙伴们应该已经完全掌握了。

我是沉默王二,一枚有趣的程序员。如果觉得文章对你有点帮助,请微信搜索「 沉默王二 」第一时间阅读。

本文 GitHub 已经收录,有大厂面试完整考点,欢迎 Star。

原创不易,莫要白票,请你为本文点个赞吧,这将是我写作更多优质文章的最强动力。

Java 数组最佳指南,快收藏让它吃灰的更多相关文章

  1. 五分钟学Java:打印Java数组最优雅的方式是什么?

    在逛 Stack Overflow 的时候,发现了一些访问量像‎安第斯山一样高的问题,比如说这个:打印 Java 数组最优雅的方式是什么?访问量足足有 220W+,想不到啊,这么简单的问题竟然有这么多 ...

  2. Java学习笔记(四)——google java编程风格指南(上)

    [前面的话] 年后开始正式上班,计划着想做很多事情,但是总会有这样那样的打扰,不知道是自己要求太高还是自我的奋斗意识不够?接下来好好加油.好好学学技术,好好学习英语,好好学习做点自己喜欢的事情,趁着自 ...

  3. Java学习笔记(五)——google java编程风格指南(中)

    [前面的话] 年后开始正式上班,计划着想做很多事情,但是总会有这样那样的打扰,不知道是自己要求太高还是自我的奋斗意识不够?接下来好好加油.好好学学技术,好好学习英语,好好学习做点自己喜欢的事情,趁着自 ...

  4. 10个精妙的Java编码最佳实践

    这是一个比Josh Bloch的Effective Java规则更精妙的10条Java编码实践的列表.和Josh Bloch的列表容易学习并且关注日常情况相比,这个列表将包含涉及API/SPI设计中不 ...

  5. 最重要的 Java EE 最佳实践

    參考:IBM WebSphere 开发人员技术期刊: 最重要的 Java EE 最佳实践 IBM WebSphere 开发人员技术期刊: 最重要的 Java EE 最佳实践 2004 年 IBM® W ...

  6. java 数组复制

    http://www.cnblogs.com/zhengbin/p/5671403.html http://www.cnblogs.com/jjdcxy/p/5870524.html Java数组拷贝 ...

  7. Java初学者最佳的学习方法以及会遇到的坑(内含学习资料)!

    最近系统整理了一套java初学者最佳的学习方法以及会遇到的坑等,希望对你有所帮助. 目录: 一.学习java的前提 二.学习java的方法 三.学习java时的坑 四.学习java的路线(画重点) 一 ...

  8. Java工程师学习指南 完结篇

    Java工程师学习指南 完结篇 先声明一点,文章里面不会详细到每一步怎么操作,只会提供大致的思路和方向,给大家以启发,如果真的要一步一步指导操作的话,那至少需要一本书的厚度啦. 因为笔者还只是一名在校 ...

  9. Java工程师学习指南 入门篇

    Java工程师学习指南 入门篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我之前写的文章都 ...

随机推荐

  1. 06 . Python3入门之IO编程(文件操作)

    IO编程简介 IO在计算机中指Input/Output,也就是输入和输出.由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口 ...

  2. Java实现蓝桥杯打印图形

    标题:打印图形 如下的程序会在控制台绘制分形图(就是整体与局部自相似的图形). 当n=1,2,3的时候,输出如下: 请仔细分析程序,并填写划线部分缺少的代码. n=1时: o ooo o n=2时: ...

  3. Java实现 LeetCode70 爬楼梯

    70. 爬楼梯 假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数. 示例 1: 输入: 2 输出: ...

  4. Java实现 LeetCode 45 跳跃游戏 II(二)

    45. 跳跃游戏 II 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃次数到达数组的最后一个位置. 示例: 输入: [ ...

  5. CSDN如何获得2020技术圈认证(新徽章哦)

    打开CSDN APP 然后登陆上就可以了 把这些看完了就可以了

  6. 搞清楚C语言指针

    Part 0:为什么要写这篇文章 C语言中的指针是C语言的精髓,也是C语言的重难点之一. 然而,很少有教程能把指针讲的初学者能听懂,还不会引起歧义. 本文章会尝试做到这一点,如有错误,请指出. Par ...

  7. mysql基础-数据库表的管理-记录(四)

    0x01 MySQL中字符大小写 1.SQL关键字及函数不区分大小写 2.数据库.表及视图名称的大小写区分与否取决于底层OS及FS 3.存储过程.存储函数及事件调度器的名字不区分大小写,但触发器区分大 ...

  8. 制作seata docker镜像

    seata是阿里巴巴的一款开源的分布式事务框架,官方已经支持docker了,但是因为业务的需要,需要自己定制. 制作docker镜像 官方的Dockerfile.下载seata-server-1.1. ...

  9. 7. redux

    1.所有的状态统一放在state中,由store来管理state 2.用户触发一个action行为,由dispatch分发action行为 3.通过store把原有的state的状态值和dispatc ...

  10. 机器学习——手把手教你用Python实现回归树模型

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天这篇是机器学习专题的第24篇文章,我们来聊聊回归树模型. 所谓的回归树模型其实就是用树形模型来解决回归问题,树模型当中最经典的自然还是决 ...