Array Operations in Java – Java中的数组操作

最后修改: 2018年 11月 6日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Any Java developer knows that producing a clean, efficient solution when working with array operations isn’t always easy to achieve. Still, they’re a central piece in the Java ecosystem – and we’ll have to deal with them on several occasions.

任何一个Java开发者都知道,在处理数组操作时,产生一个干净、高效的解决方案并不总是容易实现的。尽管如此,它们仍然是Java生态系统中的一个核心部分–而且我们将不得不在多个场合与它们打交道。

For this reason, it’s good to have a ‘cheat sheet’ – a summary of the most common procedures to help us tackle the puzzle quickly. This tutorial will come in handy in those situations.

出于这个原因,有一张 “小抄 “是很好的,即对最常见的程序进行总结,以帮助我们快速解决难题。在这些情况下,本教程将派上用场。

2. Arrays and Helper Classes

2.数组和辅助类

Before proceeding, it’s useful to understand what is an array in Java, and how to use it. If it’s your first time working with it in Java, we suggest having a look at this previous post where we covered all basic concepts.

在继续之前,了解什么是Java中的数组,以及如何使用它是非常有用的。如果你是第一次在Java中使用数组,我们建议你看一下之前的文章,在那里我们涵盖了所有的基本概念。

Please note that the basic operations that an array supports are, in a certain way, limited. It’s not uncommon to see complex algorithms to execute relatively simple tasks when it comes to arrays.

请注意,从某种程度上说,数组支持的基本操作是有限的。当涉及到数组时,看到复杂的算法来执行相对简单的任务是很常见的。

For this reason, for most of our operations, we’ll be using helper classes and methods to assist us: the Arrays class provided by Java and the Apache’s ArrayUtils one.

由于这个原因,对于我们的大部分操作,我们将使用辅助类和方法来协助我们:Java提供的Arrays类和Apache的ArrayUtils一。

To include the latter in our project, we’ll have to add the Apache Commons dependency:

为了在我们的项目中包含后者,我们必须添加Apache Commons的依赖。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

We can check out the latest version of this artifact on Maven Central.

我们可以在Maven中心上查看该工件的最新版本

3. Get the First and Last Element of an Array

3.获取一个数组的第一个和最后一个元素

This is one of the most common and simple tasks thanks to the access-by-index nature of arrays.

由于数组的按索引访问特性,这是最常见和最简单的任务之一。

Let’s start by declaring and initializing an int array that will be used in all our examples (unless we specify otherwise):

让我们先声明并初始化一个int数组,它将在我们所有的例子中使用(除非我们另有规定)。

int[] array = new int[] { 3, 5, 2, 5, 14, 4 };

Knowing that the first item of an array is associated with the index value 0 and that it has a length attribute that we can use, then it’s simple to figure out how we can get these two elements:

知道数组的第一个项目与索引值0相关,并且它有一个我们可以使用的length属性,那么我们就很容易弄清楚如何获得这两个元素。

int firstItem = array[0];
int lastItem = array[array.length - 1];

4. Get a Random Value from an Array

4.从数组中获取一个随机值

By using the java.util.Random object we can easily get any value from our array:

通过使用java.util.Random对象,我们可以轻松地从我们的数组中获得任何值。

int anyValue = array[new Random().nextInt(array.length)];

5. Append a New Item to an Array

5.在数组中添加新项目

As we know, arrays hold a fixed size of values. Therefore, we can’t just add an item and exceed this limit.

正如我们所知,数组持有固定大小的值。因此,我们不能随便增加一个项目而超过这个限制。

We’ll need to start by declaring a new, larger array, and copy the elements of the base array to the second one.

我们需要先声明一个新的、更大的数组,并将基数组的元素复制到第二个数组中。

Fortunately, the Arrays class provides a handy method to replicate the values of an array to a new different-sized structure:

幸运的是,Arrays类提供了一个方便的方法来将一个数组的值复制到一个新的不同大小的结构中。

int[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[newArray.length - 1] = newItem;

Optionally, if the ArrayUtils class is accessible in our project, we can make use of its add method (or its addAll alternative) to accomplish our objective in a one-line statement:

另外,如果我们的项目中可以访问ArrayUtils类,我们可以利用其add方法(或其addAll替代方法)来完成我们的目标,只需一行语句:

int[] newArray = ArrayUtils.add(array, newItem);

As we can imagine, this method doesn’t modify the original array object; we have to assign its output to a new variable.

我们可以想象,这个方法并没有修改原来的array对象;我们必须把它的输出分配给一个新的变量。

6. Insert a Value Between Two Values

6.在两个值之间插入一个值

Because of its indexed-values character, inserting an item in an array between two others is not a trivial job.

由于其索引值的特点,在一个数组中的两个项目之间插入一个项目并不是一个简单的工作。

Apache considered this a typical scenario and implemented a method in its ArrayUtils class to simplify the solution:

Apache认为这是一个典型的情况,并在其ArrayUtils类中实现了一个方法来简化解决方案。

int[] largerArray = ArrayUtils.insert(2, array, 77);

We have to specify the index in which we want to insert the value, and the output will be a new array containing a larger number of elements.

我们必须指定要插入值的索引,而输出将是一个包含更多元素的新数组。

The last argument is a variable argument (a.k.a. vararg) thus we can insert any number of items in the array.

最后一个参数是一个变量参数(又称vararg),因此我们可以在数组中插入任意数量的项目。

7. Compare Two Arrays

7.比较两个数组

Even though arrays are Objects and therefore provide an equals method, they use the default implementation of it, relying only on reference equality.

尽管数组是Objects,因此提供了一个equals方法,但它们使用了它的默认实现,只依赖于引用平等。

We can anyhow invoke the java.util.Arraysequals method to check if two array objects contain the same values:

无论如何,我们可以调用java.util.Arraysequals方法来检查两个数组对象是否包含相同的值。

boolean areEqual = Arrays.equals(array1, array2);

Note: this method is not effective for jagged arrays. The appropriate method to verify multi-dimensional structures’ equality is the Arrays.deepEquals one.

注意:这个方法对jagged arrays无效。验证多维结构平等的适当方法是Arrays.deepEquals

8. Check if an Array Is Empty

8.检查一个数组是否是空的

This is an uncomplicated assignment having in mind that we can use the length attribute of arrays:

这是一个不复杂的赋值,因为我们可以使用数组的length属性。

boolean isEmpty = array == null || array.length == 0;

Moreover, we also have a null-safe method in the ArrayUtils helper class that we can use:

此外,我们在ArrayUtils辅助类中也有一个防空方法,我们可以使用。

boolean isEmpty = ArrayUtils.isEmpty(array);

This function still depends on the length of the data structure, which considers nulls and empty sub-arrays as valid values too, so we’ll have to keep an eye on these edge cases:

这个函数仍然依赖于数据结构的长度,它将空值和空子数也视为有效值,所以我们必须关注这些边缘情况:

// These are empty arrays
Integer[] array1 = {};
Integer[] array2 = null;
Integer[] array3 = new Integer[0];

// All these will NOT be considered empty
Integer[] array3 = { null, null, null };
Integer[][] array4 = { {}, {}, {} };
Integer[] array5 = new Integer[3];

9. How to Shuffle the Elements of an Array

9.如何对数组中的元素进行洗牌

In order to shuffle the items in an array, we can use the ArrayUtil‘s feature:

为了对数组中的项目进行洗牌,我们可以使用ArrayUtil的功能。

ArrayUtils.shuffle(array);

This is a void method and operates on the actual values of the array.

这是一个void方法,对数组的实际值进行操作。

10. Box and Unbox Arrays

10.盒式和非盒式阵列

We often come across methods that support only Object-based arrays.

我们经常遇到只支持基于Object的数组的方法。

Again the ArrayUtils helper class comes in handy to get a boxed version of our primitive array:

再一次,ArrayUtils辅助类派上了用场,以获得原始数组的盒装版本。

Integer[] list = ArrayUtils.toObject(array);

The inverse operation is also possible:

反向操作也是可能的。

Integer[] objectArray = { 3, 5, 2, 5, 14, 4 };
int[] array = ArrayUtils.toPrimitive(objectArray);

11. Remove Duplicates from an Array

11.从阵列中删除重复的内容

The easiest way of removing duplicates is by converting the array to a Set implementation.

去除重复的最简单方法是将数组转换为Set实现。

As we may know, Collections use Generics and hence don’t support primitive types.

我们可能知道,Collections使用泛型,因此不支持原始类型。

For this reason, if we’re not handling object-based arrays as in our example, we’ll first need to box our values:

由于这个原因,如果我们不是像我们的例子中那样处理基于对象的数组,我们首先需要对我们的值进行装箱。

// Box
Integer[] list = ArrayUtils.toObject(array);
// Remove duplicates
Set<Integer> set = new HashSet<Integer>(Arrays.asList(list));
// Create array and unbox
return ArrayUtils.toPrimitive(set.toArray(new Integer[set.size()]));

Note: we can use other techniques to convert between an array and a Set object as well.

注意:我们也可以使用其他技术在数组和Set对象之间转换

Also, if we need to preserve the order of our elements, we must use a different Set implementation, such as a LinkedHashSet.

另外,如果我们需要保留元素的顺序,我们必须使用不同的Set实现,例如LinkedHashSet

12. How to Print an Array

12.如何打印一个阵列

Same as with the equals method, the array’s toString function uses the default implementation provided by the Object class, which isn’t very useful.

equals方法一样,数组的toString函数使用Object类提供的默认实现,这不是很有用。

Both Arrays and ArrayUtils classes ship with their implementations to convert the data structures to a readable String.

ArraysArrayUtils类都带有它们的实现,可以将数据结构转换成可读的String

Apart from the slightly different format they use, the most important distinction is how they treat multi-dimensional objects.

除了它们使用的格式略有不同外,最重要的区别是它们如何处理多维对象。

The Java Util’s class provides two static methods we can use:

Java Util的类提供了两个静态方法,我们可以使用。

  • toString: doesn’t work well with jagged arrays
  • deepToString: supports any Object-based arrays but doesn’t compile with primitive array arguments

On the other hand, Apache’s implementation offers a single toString method that works correctly in any case:

另一方面,Apache的实现提供了一个单一的toString方法,在任何情况下都能正常工作:

String arrayAsString = ArrayUtils.toString(array);

13. Map an Array to Another Type

13.将一个数组映射到另一个类型

It’s often useful to apply operations on all array items, possibly converting them to another type of object.

对所有数组项进行操作,可能会将它们转换为另一种类型的对象,这通常是很有用的。

With this objective in mind, we’ll try to create a flexible helper method using Generics:

考虑到这个目标,我们将尝试使用泛型来创建一个灵活的辅助方法:

public static <T, U> U[] mapObjectArray(
  T[] array, Function<T, U> function,
  Class<U> targetClazz) {
    U[] newArray = (U[]) Array.newInstance(targetClazz, array.length);
    for (int i = 0; i < array.length; i++) {
        newArray[i] = function.apply(array[i]);
    }
    return newArray;
}

If we don’t use Java 8 in our project, we can discard the Function argument, and create a method for each mapping that we need to carry out.

如果我们在项目中不使用Java 8,我们可以放弃Function参数,并为我们需要进行的每一个映射创建一个方法。

We can now reuse our generic method for different operations. Let’s create two test cases to illustrate this:

现在我们可以重复使用我们的通用方法来进行不同的操作。让我们创建两个测试案例来说明这一点。

@Test
public void whenMapArrayMultiplyingValues_thenReturnMultipliedArray() {
    Integer[] multipliedExpectedArray = new Integer[] { 6, 10, 4, 10, 28, 8 };
    Integer[] output = 
      MyHelperClass.mapObjectArray(array, value -> value * 2, Integer.class);

    assertThat(output).containsExactly(multipliedExpectedArray);
}

@Test
public void whenMapDividingObjectArray_thenReturnMultipliedArray() {
    Double[] multipliedExpectedArray = new Double[] { 1.5, 2.5, 1.0, 2.5, 7.0, 2.0 };
    Double[] output =
      MyHelperClass.mapObjectArray(array, value -> value / 2.0, Double.class);

    assertThat(output).containsExactly(multipliedExpectedArray);
}

For primitive types, we’ll have to box our values first.

对于原始类型,我们必须首先对我们的值进行装箱。

As an alternative, we can turn to Java 8’s Streams to carry out the mapping for us.

作为替代方案,我们可以求助于Java 8 的 Streams来为我们进行映射。

We’ll need to transform the array into a Stream of Objects first. We can do so with the Arrays.stream method.

我们需要首先将数组转换为Objects的Stream。我们可以用Arrays.stream方法来做。

For example, if we want to map our int values to a custom String representation, we’ll implement this:

例如,如果我们想把我们的int值映射到一个自定义的String表示法,我们将这样实现。

String[] stringArray = Arrays.stream(array)
  .mapToObj(value -> String.format("Value: %s", value))
  .toArray(String[]::new);

14. Filter Values in an Array

14.阵列中的过滤值

Filtering out values from a collection is a common task that we might have to perform in more than one occasion.

从一个集合中过滤出数值是一项常见的任务,我们可能要在不止一个场合中执行。

This is because at the time we create the array that will receive the values, we can’t be sure of its final size. Therefore, we’ll rely on the Streams approach again.

这是因为在我们创建将接收这些值的数组时,我们无法确定其最终大小。因此,我们将再次依赖Streams方法。

Imagine we want to remove all odd numbers from an array:

想象一下,我们想从一个数组中删除所有的奇数。

int[] evenArray = Arrays.stream(array)
  .filter(value -> value % 2 == 0)
  .toArray();

15. Other Common Array Operations

15.其他常见的阵列操作

There are, of course, plenty of other array operations that we might need to perform.

当然,还有很多其他的数组操作,我们可能需要执行。

Apart from the ones shown in this tutorial, we’ve extensively covered other operations in the dedicated posts:

除了本教程中展示的那些,我们在专门的帖子中广泛地介绍了其他操作。

16. Conclusion

16.结语

Arrays are one of the core functionalities of Java, and therefore it’s really important to understand how they work and to know what we can and can’t do with them.

数组是Java的核心功能之一,因此,了解它们的工作原理,知道我们能用它们做什么,不能做什么,真的很重要。

In this tutorial, we learned how we can handle array operations appropriately in common scenarios.

在本教程中,我们学习了如何在常见的情况下适当地处理数组操作。

As always, the full source code of the working examples is available over on our Github repo.

一如既往,工作实例的完整源代码可在我们的Github repo上获得。