如何在不影响原始数组的情况下从 Java 数组创建可变列表

JavaJavaBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

Introduction

In Java programming, working with different data structures is essential for efficient program development. Arrays and Lists are two fundamental data structures that serve different purposes. While arrays provide fixed-size storage, Lists offer flexibility with dynamic sizing and additional utility methods.

This tutorial focuses on converting Java arrays into mutable Lists without modifying the original array data. By the end of this lab, you will understand the relationship between arrays and Lists and learn practical techniques to convert between them while maintaining data integrity.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/DataStructuresGroup(["Data Structures"]) java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) java/DataStructuresGroup -.-> java/arrays("Arrays") java/DataStructuresGroup -.-> java/arrays_methods("Arrays Methods") java/DataStructuresGroup -.-> java/collections_methods("Collections Methods") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/arraylist("ArrayList") subgraph Lab Skills java/arrays -.-> lab-413983{{"如何在不影响原始数组的情况下从 Java 数组创建可变列表"}} java/arrays_methods -.-> lab-413983{{"如何在不影响原始数组的情况下从 Java 数组创建可变列表"}} java/collections_methods -.-> lab-413983{{"如何在不影响原始数组的情况下从 Java 数组创建可变列表"}} java/arraylist -.-> lab-413983{{"如何在不影响原始数组的情况下从 Java 数组创建可变列表"}} end

Understanding Java Arrays and Lists

In this step, we will explore the basic differences between Java arrays and Lists by creating examples of both. We will also examine how to access and display their elements.

Creating a Java Project

Let's start by creating a simple Java file to work with:

  1. Open the terminal and navigate to the project directory:

    cd ~/project
  2. Create a new directory for our Java files:

    mkdir -p src/main/java
    cd src/main/java
  3. Create a new Java file called ArrayListDemo.java using the WebIDE. Click on the Explorer icon in the WebIDE, navigate to project/src/main/java, right-click, and select "New File". Name it ArrayListDemo.java.

  4. Add the following code to the file:

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {
    public static void main(String[] args) {
        System.out.println("Java Arrays vs Lists Example");
        System.out.println("============================");

        // Creating an array of integers
        int[] numbersArray = {1, 2, 3, 4, 5};

        // Displaying array contents
        System.out.println("Array contents: " + Arrays.toString(numbersArray));

        // Accessing array elements by index
        System.out.println("Array element at index 2: " + numbersArray[2]);

        // Creating a List using ArrayList
        List<Integer> numbersList = new ArrayList<>();
        numbersList.add(1);
        numbersList.add(2);
        numbersList.add(3);
        numbersList.add(4);
        numbersList.add(5);

        // Displaying list contents
        System.out.println("List contents: " + numbersList);

        // Accessing list elements by index
        System.out.println("List element at index 2: " + numbersList.get(2));

        // Demonstrating List flexibility - adding a new element
        numbersList.add(6);
        System.out.println("List after adding element: " + numbersList);

        // Demonstrating List flexibility - removing an element
        numbersList.remove(1); // Removes element at index 1
        System.out.println("List after removing element at index 1: " + numbersList);
    }
}
  1. Compile and run the Java program:
    cd ~/project
    javac src/main/java/ArrayListDemo.java
    java -cp src/main/java ArrayListDemo

You should see output similar to this:

Java Arrays vs Lists Example
============================
Array contents: [1, 2, 3, 4, 5]
Array element at index 2: 3
List contents: [1, 2, 3, 4, 5]
List element at index 2: 3
List after adding element: [1, 2, 3, 4, 5, 6]
List after removing element at index 1: [1, 3, 4, 5, 6]

Key Differences Between Arrays and Lists

  1. Size flexibility:

    • Arrays have a fixed size that cannot be changed after creation
    • Lists can dynamically grow or shrink as needed
  2. Available operations:

    • Arrays have limited built-in functionality
    • Lists provide numerous methods for adding, removing, and manipulating elements
  3. Type constraints:

    • Arrays can store primitives or objects
    • Lists can only store objects (but autoboxing allows storing primitives indirectly)

Understanding these differences helps you choose the right data structure for your specific needs.

Converting Arrays to Lists

Now that we understand the basics of arrays and Lists, let's explore different ways to convert an array to a List. We'll focus particularly on creating mutable Lists from arrays.

Create a New Java File

  1. Create a new Java file called ArrayToListConversion.java in the same directory:

    From the WebIDE, navigate to project/src/main/java, right-click, and select "New File". Name it ArrayToListConversion.java.

  2. Add the following code to the file:

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ArrayToListConversion {
    public static void main(String[] args) {
        System.out.println("Converting Arrays to Lists");
        System.out.println("=========================");

        // Create an array of Strings
        String[] fruitsArray = {"Apple", "Banana", "Cherry", "Date", "Elderberry"};
        System.out.println("Original array: " + Arrays.toString(fruitsArray));

        // Method 1: Using Arrays.asList() - Creates a fixed-size list
        System.out.println("\nMethod 1: Arrays.asList()");
        List<String> fruitsListFixed = Arrays.asList(fruitsArray);
        System.out.println("List created with Arrays.asList(): " + fruitsListFixed);

        // Try to modify the list
        System.out.println("Changing element at index 0 to 'Apricot'");
        fruitsListFixed.set(0, "Apricot");
        System.out.println("List after change: " + fruitsListFixed);
        System.out.println("Original array after List change: " + Arrays.toString(fruitsArray));

        // This will cause UnsupportedOperationException
        try {
            System.out.println("Trying to add a new element to the fixed-size list...");
            fruitsListFixed.add("Fig");
        } catch (UnsupportedOperationException e) {
            System.out.println("Error: Cannot add to fixed-size list!");
        }

        // Reset the array for next example
        fruitsArray[0] = "Apple";

        // Method 2: Using new ArrayList<>(Arrays.asList()) - Creates a mutable list
        System.out.println("\nMethod 2: new ArrayList<>(Arrays.asList())");
        List<String> fruitsList = new ArrayList<>(Arrays.asList(fruitsArray));
        System.out.println("List created with new ArrayList<>(Arrays.asList()): " + fruitsList);

        // Modify the list
        System.out.println("Adding 'Fig' to the list");
        fruitsList.add("Fig");
        System.out.println("List after adding 'Fig': " + fruitsList);
        System.out.println("Original array after List modification: " + Arrays.toString(fruitsArray));

        // Method 3: Using Stream API (Java 8+)
        System.out.println("\nMethod 3: Using Stream API");
        List<String> fruitsListStream = Arrays.stream(fruitsArray)
                                              .collect(Collectors.toList());
        System.out.println("List created with Stream API: " + fruitsListStream);

        // Modify the list
        System.out.println("Adding 'Grape' to the list");
        fruitsListStream.add("Grape");
        System.out.println("List after adding 'Grape': " + fruitsListStream);
        System.out.println("Original array after List modification: " + Arrays.toString(fruitsArray));
    }
}
  1. Compile and run the program:
    cd ~/project
    javac src/main/java/ArrayToListConversion.java
    java -cp src/main/java ArrayToListConversion

You should see output similar to this:

Converting Arrays to Lists
=========================
Original array: [Apple, Banana, Cherry, Date, Elderberry]

Method 1: Arrays.asList()
List created with Arrays.asList(): [Apple, Banana, Cherry, Date, Elderberry]
Changing element at index 0 to 'Apricot'
List after change: [Apricot, Banana, Cherry, Date, Elderberry]
Original array after List change: [Apricot, Banana, Cherry, Date, Elderberry]
Trying to add a new element to the fixed-size list...
Error: Cannot add to fixed-size list!

Method 2: new ArrayList<>(Arrays.asList())
List created with new ArrayList<>(Arrays.asList()): [Apple, Banana, Cherry, Date, Elderberry]
Adding 'Fig' to the list
List after adding 'Fig': [Apple, Banana, Cherry, Date, Elderberry, Fig]
Original array after List modification: [Apple, Banana, Cherry, Date, Elderberry]

Method 3: Using Stream API
List created with Stream API: [Apple, Banana, Cherry, Date, Elderberry]
Adding 'Grape' to the list
List after adding 'Grape': [Apple, Banana, Cherry, Date, Elderberry, Grape]
Original array after List modification: [Apple, Banana, Cherry, Date, Elderberry]

Understanding the Different Conversion Methods

  1. Arrays.asList():

    • Creates a fixed-size list backed by the original array
    • The list size cannot be changed (no adding/removing elements)
    • Changes to the list elements affect the original array
  2. new ArrayList<>(Arrays.asList()):

    • Creates a new ArrayList that contains all elements from the array
    • The list is mutable (can add/remove elements)
    • Changes to the list do not affect the original array
  3. Stream API (Java 8+):

    • A more modern approach using functional programming
    • Creates a completely independent list
    • Offers flexibility to perform transformations during conversion

For beginners, the second method (new ArrayList<>(Arrays.asList())) is generally the most useful because it creates a fully mutable list without affecting the original array.

Working with Primitive Arrays

In the previous steps, we worked with arrays of reference types (String). However, Java arrays can also contain primitive types such as int, double, etc. Converting primitive arrays to Lists requires additional steps because Java generics only work with reference types.

Let's create a new example to demonstrate this process.

Create a New Java File

  1. Create a new Java file called PrimitiveArrayToList.java in the same directory:

    From the WebIDE, navigate to project/src/main/java, right-click, and select "New File". Name it PrimitiveArrayToList.java.

  2. Add the following code to the file:

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class PrimitiveArrayToList {
    public static void main(String[] args) {
        System.out.println("Converting Primitive Arrays to Lists");
        System.out.println("===================================");

        // Create a primitive int array
        int[] numbersArray = {10, 20, 30, 40, 50};
        System.out.println("Original primitive array: " + Arrays.toString(numbersArray));

        // Method 1: Manual conversion
        System.out.println("\nMethod 1: Manual conversion");
        List<Integer> numbersList1 = new ArrayList<>();
        for (int number : numbersArray) {
            numbersList1.add(number); // Autoboxing converts int to Integer
        }
        System.out.println("List after manual conversion: " + numbersList1);

        // Method 2: Using Java 8 Streams
        System.out.println("\nMethod 2: Using Java 8 Streams");
        List<Integer> numbersList2 = Arrays.stream(numbersArray)
                                          .boxed() // Converts IntStream to Stream<Integer>
                                          .collect(Collectors.toList());
        System.out.println("List after stream conversion: " + numbersList2);

        // Modify the lists to demonstrate independence from the array
        System.out.println("\nModifying the lists:");
        numbersList1.add(60);
        numbersList1.set(0, 15);
        numbersList2.add(70);
        numbersList2.remove(0);

        System.out.println("List 1 after modifications: " + numbersList1);
        System.out.println("List 2 after modifications: " + numbersList2);
        System.out.println("Original array after List modifications: " + Arrays.toString(numbersArray));

        // Create and convert other primitive type arrays
        System.out.println("\nOther primitive type examples:");

        double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5};
        List<Double> doubleList = Arrays.stream(doubleArray)
                                      .boxed()
                                      .collect(Collectors.toList());
        System.out.println("Double array: " + Arrays.toString(doubleArray));
        System.out.println("Double list: " + doubleList);

        boolean[] boolArray = {true, false, true, true, false};
        List<Boolean> boolList = new ArrayList<>();
        for (boolean value : boolArray) {
            boolList.add(value);
        }
        System.out.println("Boolean array: " + Arrays.toString(boolArray));
        System.out.println("Boolean list: " + boolList);
    }
}
  1. Compile and run the program:
    cd ~/project
    javac src/main/java/PrimitiveArrayToList.java
    java -cp src/main/java PrimitiveArrayToList

You should see output similar to this:

Converting Primitive Arrays to Lists
===================================
Original primitive array: [10, 20, 30, 40, 50]

Method 1: Manual conversion
List after manual conversion: [10, 20, 30, 40, 50]

Method 2: Using Java 8 Streams
List after stream conversion: [10, 20, 30, 40, 50]

Modifying the lists:
List 1 after modifications: [15, 20, 30, 40, 50, 60]
List 2 after modifications: [20, 30, 40, 50, 70]
Original array after List modifications: [10, 20, 30, 40, 50]

Other primitive type examples:
Double array: [1.1, 2.2, 3.3, 4.4, 5.5]
Double list: [1.1, 2.2, 3.3, 4.4, 5.5]
Boolean array: [true, false, true, true, false]
Boolean list: [true, false, true, true, false]

Understanding Primitive Array Conversion

When working with primitive arrays, there are two key considerations:

  1. Autoboxing: Java automatically converts primitive values to their wrapper class objects when adding to collections. For example, int is converted to Integer.

  2. Boxing Methods for Streams: When using streams with primitive arrays, you need to call the .boxed() method to convert primitive streams to object streams.

The conversion process creates completely new Lists that are independent of the original arrays. This means:

  1. Modifying the List will not affect the original array
  2. The Lists are fully mutable (you can add, remove, or change elements)
  3. Each element in the List is a new object (wrapper) that contains the value from the array

This independence is particularly useful when you need to manipulate the data without risking changes to the original array.

Practical Applications and Common Pitfalls

Now that we understand how to convert arrays to mutable Lists, let's explore some practical applications and common pitfalls. We'll create a final example that demonstrates real-world use cases and how to avoid common mistakes.

Create a New Java File

  1. Create a new Java file called ArrayListPractical.java in the same directory:

    From the WebIDE, navigate to project/src/main/java, right-click, and select "New File". Name it ArrayListPractical.java.

  2. Add the following code to the file:

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;

public class ArrayListPractical {
    public static void main(String[] args) {
        System.out.println("Practical Applications and Common Pitfalls");
        System.out.println("========================================");

        // Use case 1: Creating a mutable list from a configuration array
        String[] defaultSettings = {"dark-mode", "auto-save", "notifications"};
        System.out.println("Default settings array: " + Arrays.toString(defaultSettings));

        // Create a mutable list of user settings starting with defaults
        List<String> userSettings = new ArrayList<>(Arrays.asList(defaultSettings));
        System.out.println("Initial user settings list: " + userSettings);

        // User can add or remove settings
        userSettings.add("sync-enabled");
        userSettings.remove("notifications");
        System.out.println("Modified user settings: " + userSettings);
        System.out.println("Original default settings (unchanged): " + Arrays.toString(defaultSettings));

        // Use case 2: Working with legacy APIs that return arrays
        System.out.println("\nUse case: Working with legacy APIs");

        // Simulate a legacy API that returns an array
        String[] legacyData = getLegacyData();
        System.out.println("Data from legacy API (array): " + Arrays.toString(legacyData));

        // Convert to a mutable list for modern processing
        List<String> modernData = new ArrayList<>(Arrays.asList(legacyData));

        // Process with modern methods
        modernData.removeIf(item -> item.startsWith("OLD_"));
        modernData.replaceAll(item -> item.toLowerCase());
        System.out.println("Processed data (list): " + modernData);

        // Common Pitfall 1: Forgetting that Arrays.asList creates a fixed-size list
        System.out.println("\nCommon Pitfall 1: Fixed-size list");
        Integer[] numberArray = {1, 2, 3, 4, 5};

        // This creates a fixed-size list
        List<Integer> wrongWay = Arrays.asList(numberArray);
        System.out.println("Fixed-size list: " + wrongWay);

        try {
            wrongWay.add(6); // This will fail
        } catch (UnsupportedOperationException e) {
            System.out.println("Error: Cannot add to fixed-size list!");
        }

        // Correct way
        List<Integer> rightWay = new ArrayList<>(Arrays.asList(numberArray));
        rightWay.add(6); // This works fine
        System.out.println("Mutable list: " + rightWay);

        // Common Pitfall 2: Array of primitives
        System.out.println("\nCommon Pitfall 2: Array of primitives");
        int[] primitiveArray = {10, 20, 30};

        // This won't compile: Arrays.asList(primitiveArray)
        // List<Integer> primitiveList = Arrays.asList(primitiveArray);

        // Correct ways
        List<Integer> primitiveList1 = new ArrayList<>();
        for (int value : primitiveArray) {
            primitiveList1.add(value);
        }

        List<Integer> primitiveList2 = Arrays.stream(primitiveArray)
                                            .boxed()
                                            .toList();

        System.out.println("Primitive array: " + Arrays.toString(primitiveArray));
        System.out.println("Converted list (loop method): " + primitiveList1);
        System.out.println("Converted list (stream method): " + primitiveList2);
    }

    // Simulate a legacy API that returns an array
    private static String[] getLegacyData() {
        return new String[] {"OLD_RECORD1", "ACTIVE_DATA", "OLD_RECORD2", "CURRENT_INFO"};
    }
}
  1. Compile and run the program:
    cd ~/project
    javac src/main/java/ArrayListPractical.java
    java -cp src/main/java ArrayListPractical

You should see output similar to this:

Practical Applications and Common Pitfalls
========================================
Default settings array: [dark-mode, auto-save, notifications]
Initial user settings list: [dark-mode, auto-save, notifications]
Modified user settings: [dark-mode, auto-save, sync-enabled]
Original default settings (unchanged): [dark-mode, auto-save, notifications]

Use case: Working with legacy APIs
Data from legacy API (array): [OLD_RECORD1, ACTIVE_DATA, OLD_RECORD2, CURRENT_INFO]
Processed data (list): [active_data, current_info]

Common Pitfall 1: Fixed-size list
Fixed-size list: [1, 2, 3, 4, 5]
Error: Cannot add to fixed-size list!
Mutable list: [1, 2, 3, 4, 5, 6]

Common Pitfall 2: Array of primitives
Primitive array: [10, 20, 30]
Converted list (loop method): [10, 20, 30]
Converted list (stream method): [10, 20, 30]

Key Takeaways from Practical Applications

  1. Configuration Management: Using arrays for default settings and Lists for user-specific settings allows for flexibility while preserving defaults.

  2. Working with Legacy APIs: Many older Java APIs return arrays, which you can convert to Lists to leverage modern Java features like lambda expressions and method references.

  3. Common Pitfalls to Avoid:

    • Remember that Arrays.asList() creates a fixed-size list that cannot grow or shrink
    • Primitive arrays cannot be directly passed to Arrays.asList() - you must use loops or streams with boxing
  4. Best Practice: For most use cases, the safest approach is using new ArrayList<>(Arrays.asList(array)) for object arrays and stream methods for primitive arrays.

By understanding these patterns and pitfalls, you can effectively work with both arrays and Lists in your Java applications, choosing the right data structure and conversion method for each situation.

Summary

In this lab, you have learned how to create mutable Lists from Java arrays while preserving the original array data. You have explored:

  1. The fundamental differences between Java arrays and Lists, including their structure, capabilities, and use cases.

  2. Multiple methods to convert arrays to Lists:

    • Using Arrays.asList() for a fixed-size view of the array
    • Using new ArrayList<>(Arrays.asList()) for a fully mutable List
    • Using Stream API with .boxed() for primitive arrays
  3. Special considerations for primitive arrays, which require explicit boxing to convert to Lists of wrapper objects.

  4. Practical applications and common pitfalls when working with arrays and Lists, including configuration management and interacting with legacy APIs.

These techniques are valuable in many real-world Java applications where you need to work with both arrays and Lists, particularly when you need to preserve original data while creating flexible, mutable collections for manipulation. Understanding the relationship between arrays and Lists allows you to write more efficient, maintainable Java code.