HOWTO · Java

如何向 Java 数组添加元素

了解 Java 数组为何没有内置的 push() 方法,以及如何使用堆栈、列表或数组复制来添加值。

本页内容

Java 数组的长度是固定的,并且没有内置的 push() 方法。要添加一个元素,请根据它应该放置的位置选择集合:堆栈将它放在顶部,LinkedList.push() 将它插入到最前面,ArrayList.add() 将它追加到可调整大小的列表末尾。如果必须保留数组,就把它的元素复制到一个新的更长的数组中,并使用返回的数组。

以下五个 Java 示例都已在提供的运行时中成功执行,每个示例的退出代码都是 0。

使用 Stack.push() 实现后进先出行为

Stack.push() 将元素添加到后进先出(LIFO)堆栈的顶部。因此,最近压入的元素会最先被 pop() 返回。该示例压入三个字符串,然后打印并移除顶部元素。

import java.util.Stack;

public class StackExample {
  public static void main(String[] args) {
    Stack<String> stack = new Stack<>();
    stack.push("Java");
    stack.push("is");
    stack.push("useful");

    System.out.println("Top: " + stack.peek());
    System.out.println("Removed: " + stack.pop());
    System.out.println("Stack: " + stack);
  }
}
Top: useful
Removed: useful
Stack: [Java, is]

当后续的值应该先于先前的值被处理时,选择这个 API,例如跟踪撤销操作或待处理的工作。显示的堆栈包含 pop() 移除顶部元素后剩余的元素。如果只需要按原始顺序保留这些值,堆栈会增加你不需要的行为;请改用追加式的列表。

使用 LinkedList.push() 在最前面添加

LinkedList.push() 是在最前面插入,而不是追加到末尾。每次后续调用都会成为新的第一个元素,因此列表的顺序与调用顺序相反。

import java.util.LinkedList;

public class LinkedListExample {
  public static void main(String[] args) {
    LinkedList<String> values = new LinkedList<>();
    values.push("Java");
    values.push("is");
    values.push("useful");

    System.out.println(values);
  }
}
[useful, is, Java]

这同样是一种在最前面插入的操作,因此当列表被打印时,最新添加的值排在第一位。当这种顺序是有意为之(例如维护一个以最新元素位于最前面的序列)时使用它。如果任务是要在末尾添加,请改用 ArrayList.add();尽管二者都会向集合中添加元素,但结果不同。

使用 ArrayList.add() 追加到可调整大小的列表

当目标是向一个可以增长的列表追加值时,使用 ArrayList.add()。该调用将每个值添加到末尾。ArrayList 是一个集合,而不是 Java 数组,因此它不具有数组那样的固定长度。

import java.util.ArrayList;

public class ArrayListExample {
  public static void main(String[] args) {
    ArrayList<String> values = new ArrayList<>();
    values.add("Java");
    values.add("is");
    values.add("useful");

    System.out.println(values);
  }
}
[Java, is, useful]

当“向数组添加”指的是保持现有的顺序,并将另一个值放在最后一个值之后时,这是最接近的集合选择。列表可以随着值的添加而增长,add() 不需要调用方为每个新值分配并重新赋值一个更大的数组。当长度是数据形态的一部分,或其他 API 要求固定长度时,选择固定数组;当值的数量可以变化并希望在末尾追加时,选择列表。

使用自定义方法向 Java 数组追加

自定义的 push() 方法可以返回一个包含原始元素和新值的更长的数组。调用方必须赋值这个返回的数组,因为 Java 数组不能就地增长。复制所需的时间与现有元素的数量成正比,并且需要为新数组提供内存。

public class ArrayAppendExample {
  private static String[] push(String[] array, String value) {
    String[] longer = new String[array.length + 1];
    System.arraycopy(array, 0, longer, 0, array.length);
    longer[array.length] = value;
    return longer;
  }

  public static void main(String[] args) {
    String[] values = {"a", "b", "c"};
    values = push(values, "d");
    System.out.println(java.util.Arrays.toString(values));
  }
}
[a, b, c, d]

返回的引用很重要:原始数组仍然保持原始长度,而 push() 创建并返回一个单独的数组。赋值该结果后,后续读取将使用更长的数组;忽略返回值则会使调用方继续使用原始数组。当方法签名或库要求使用数组时,这种方法很有用。重复该操作每次都会复制已有的元素,因此对于一系列的添加操作,使用可调整大小的集合通常更简单,也避免了自己编写这段复制逻辑。

使用 ArrayDeque.push() 作为堆栈

对于新的堆栈代码,ArrayDeque 是一个可调整大小的双端队列,支持在任一端插入和删除。它的 push() 方法在最前面添加,pop() 从同一端移除,从而实现后进先出的行为。迭代双端队列时从最前面开始。

import java.util.ArrayDeque;

public class ArrayDequeExample {
  public static void main(String[] args) {
    ArrayDeque<String> values = new ArrayDeque<>();
    values.push("Java");
    values.push("is");
    values.push("useful");

    System.out.println("Top: " + values.peek());
    System.out.println("Removed: " + values.pop());
    System.out.println("Deque: " + values);
  }
}
Top: useful
Removed: useful
Deque: [is, Java]

对于堆栈行为,ArrayDeque.push() 提供了与 Stack.push() 展示的相同的顶部优先模型,同时双端队列也支持在其另一端进行操作。当你需要堆栈并且希望使用双端队列集合时使用它;当所需结果是一个将新元素放在末尾的有序列表时,使用 ArrayList.add()。这一选择取决于值应该放在哪里以及它们将如何被取用,而不是 Java 数组是否拥有内置的 push 操作。