Comment créer une liste mutable à partir d'un tableau Java sans affecter le tableau d'origine

JavaBeginner
Pratiquer maintenant

Introduction

En programmation Java, travailler avec différentes structures de données est essentiel pour un développement de programme efficace. Les tableaux (Arrays) et les listes (Lists) sont deux structures de données fondamentales qui servent des objectifs différents. Alors que les tableaux fournissent un stockage de taille fixe, les listes offrent une flexibilité avec un dimensionnement dynamique et des méthodes utilitaires supplémentaires.

Ce tutoriel se concentre sur la conversion des tableaux Java en listes mutables sans modifier les données du tableau d'origine. À la fin de ce lab, vous comprendrez la relation entre les tableaux et les listes et apprendrez des techniques pratiques pour convertir entre eux tout en maintenant l'intégrité des données.

Comprendre les tableaux (Arrays) et les listes (Lists) en Java

Dans cette étape, nous allons explorer les différences fondamentales entre les tableaux (Arrays) et les listes (Lists) en Java en créant des exemples des deux. Nous examinerons également comment accéder à leurs éléments et les afficher.

Création d'un projet Java

Commençons par créer un simple fichier Java pour travailler :

  1. Ouvrez le terminal et accédez au répertoire du projet :

    cd ~/project
  2. Créez un nouveau répertoire pour nos fichiers Java :

    mkdir -p src/main/java
    cd src/main/java
  3. Créez un nouveau fichier Java appelé ArrayListDemo.java en utilisant le WebIDE. Cliquez sur l'icône Explorer dans le WebIDE, accédez à project/src/main/java, faites un clic droit et sélectionnez "New File". Nommez-le ArrayListDemo.java.

  4. Ajoutez le code suivant au fichier :

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. Compilez et exécutez le programme Java :

    cd ~/project
    javac src/main/java/ArrayListDemo.java
    java -cp src/main/java ArrayListDemo

Vous devriez voir une sortie similaire à celle-ci :

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]

Différences clés entre les tableaux (Arrays) et les listes (Lists)

  1. Flexibilité de la taille :

    • Les tableaux ont une taille fixe qui ne peut pas être modifiée après la création.
    • Les listes peuvent croître ou se réduire dynamiquement selon les besoins.
  2. Opérations disponibles :

    • Les tableaux ont des fonctionnalités intégrées limitées.
    • Les listes fournissent de nombreuses méthodes pour ajouter, supprimer et manipuler des éléments.
  3. Contraintes de type :

    • Les tableaux peuvent stocker des primitives ou des objets.
    • Les listes ne peuvent stocker que des objets (mais l'autoboxing permet de stocker des primitives indirectement).

Comprendre ces différences vous aide à choisir la structure de données appropriée pour vos besoins spécifiques.

Conversion des tableaux (Arrays) en listes (Lists)

Maintenant que nous comprenons les bases des tableaux (Arrays) et des listes (Lists), explorons différentes manières de convertir un tableau en une liste. Nous nous concentrerons particulièrement sur la création de listes mutables à partir de tableaux.

Créer un nouveau fichier Java

  1. Créez un nouveau fichier Java appelé ArrayToListConversion.java dans le même répertoire :

    Depuis le WebIDE, accédez à project/src/main/java, faites un clic droit et sélectionnez "New File". Nommez-le ArrayToListConversion.java.

  2. Ajoutez le code suivant au fichier :

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. Compilez et exécutez le programme :

    cd ~/project
    javac src/main/java/ArrayToListConversion.java
    java -cp src/main/java ArrayToListConversion

Vous devriez voir une sortie similaire à celle-ci :

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]

Comprendre les différentes méthodes de conversion

  1. Arrays.asList() :

    • Crée une liste de taille fixe soutenue par le tableau d'origine.
    • La taille de la liste ne peut pas être modifiée (pas d'ajout/suppression d'éléments).
    • Les modifications apportées aux éléments de la liste affectent le tableau d'origine.
  2. new ArrayList<>(Arrays.asList()) :

    • Crée une nouvelle ArrayList qui contient tous les éléments du tableau.
    • La liste est mutable (peut ajouter/supprimer des éléments).
    • Les modifications apportées à la liste n'affectent pas le tableau d'origine.
  3. Stream API (Java 8+) :

    • Une approche plus moderne utilisant la programmation fonctionnelle.
    • Crée une liste complètement indépendante.
    • Offre la flexibilité d'effectuer des transformations pendant la conversion.

Pour les débutants, la deuxième méthode (new ArrayList<>(Arrays.asList())) est généralement la plus utile car elle crée une liste entièrement mutable sans affecter le tableau d'origine.

Travailler avec les tableaux de types primitifs

Dans les étapes précédentes, nous avons travaillé avec des tableaux de types de référence (String). Cependant, les tableaux Java peuvent également contenir des types primitifs tels que int, double, etc. La conversion des tableaux de types primitifs en listes nécessite des étapes supplémentaires car les génériques Java ne fonctionnent qu'avec des types de référence.

Créons un nouvel exemple pour illustrer ce processus.

Créer un nouveau fichier Java

  1. Créez un nouveau fichier Java appelé PrimitiveArrayToList.java dans le même répertoire :

    Depuis le WebIDE, accédez à project/src/main/java, faites un clic droit et sélectionnez "New File". Nommez-le PrimitiveArrayToList.java.

  2. Ajoutez le code suivant au fichier :

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. Compilez et exécutez le programme :

    cd ~/project
    javac src/main/java/PrimitiveArrayToList.java
    java -cp src/main/java PrimitiveArrayToList

Vous devriez voir une sortie similaire à celle-ci :

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]

Comprendre la conversion des tableaux de types primitifs

Lorsque vous travaillez avec des tableaux de types primitifs, il y a deux considérations clés :

  1. Autoboxing : Java convertit automatiquement les valeurs primitives en leurs objets de classe wrapper lors de l'ajout aux collections. Par exemple, int est converti en Integer.

  2. Méthodes de boxing pour les Streams : Lorsque vous utilisez des streams avec des tableaux de types primitifs, vous devez appeler la méthode .boxed() pour convertir les streams de types primitifs en streams d'objets.

Le processus de conversion crée des listes complètement nouvelles qui sont indépendantes des tableaux d'origine. Cela signifie que :

  1. La modification de la liste n'affectera pas le tableau d'origine.
  2. Les listes sont entièrement mutables (vous pouvez ajouter, supprimer ou modifier des éléments).
  3. Chaque élément de la liste est un nouvel objet (wrapper) qui contient la valeur du tableau.

Cette indépendance est particulièrement utile lorsque vous devez manipuler les données sans risquer de modifier le tableau d'origine.

Applications pratiques et pièges courants

Maintenant que nous comprenons comment convertir des tableaux en listes mutables, explorons quelques applications pratiques et pièges courants. Nous allons créer un dernier exemple qui démontre des cas d'utilisation réels et comment éviter les erreurs courantes.

Créer un nouveau fichier Java

  1. Créez un nouveau fichier Java appelé ArrayListPractical.java dans le même répertoire :

    Depuis le WebIDE, accédez à project/src/main/java, faites un clic droit et sélectionnez "New File". Nommez-le ArrayListPractical.java.

  2. Ajoutez le code suivant au fichier :

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. Compilez et exécutez le programme :

    cd ~/project
    javac src/main/java/ArrayListPractical.java
    java -cp src/main/java ArrayListPractical

Vous devriez voir une sortie similaire à celle-ci :

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]

Principaux points à retenir des applications pratiques

  1. Gestion de la configuration : L'utilisation de tableaux pour les paramètres par défaut et de listes pour les paramètres spécifiques à l'utilisateur permet une flexibilité tout en conservant les valeurs par défaut.

  2. Travailler avec les API héritées : De nombreuses anciennes API Java renvoient des tableaux, que vous pouvez convertir en listes pour tirer parti des fonctionnalités Java modernes telles que les expressions lambda et les références de méthode.

  3. Pièges courants à éviter :

    • N'oubliez pas que Arrays.asList() crée une liste de taille fixe qui ne peut pas croître ou rétrécir.
    • Les tableaux de types primitifs ne peuvent pas être directement passés à Arrays.asList() - vous devez utiliser des boucles ou des streams avec du boxing.
  4. Meilleure pratique : Pour la plupart des cas d'utilisation, l'approche la plus sûre consiste à utiliser new ArrayList<>(Arrays.asList(array)) pour les tableaux d'objets et les méthodes de stream pour les tableaux de types primitifs.

En comprenant ces modèles et ces pièges, vous pouvez travailler efficacement avec les tableaux et les listes dans vos applications Java, en choisissant la bonne structure de données et la bonne méthode de conversion pour chaque situation.

Résumé

Dans ce lab, vous avez appris à créer des listes mutables à partir de tableaux Java tout en préservant les données du tableau d'origine. Vous avez exploré :

  1. Les différences fondamentales entre les tableaux Java et les listes, y compris leur structure, leurs capacités et leurs cas d'utilisation.

  2. Plusieurs méthodes pour convertir des tableaux en listes :

    • Utilisation de Arrays.asList() pour une vue de taille fixe du tableau.
    • Utilisation de new ArrayList<>(Arrays.asList()) pour une liste entièrement mutable.
    • Utilisation de l'API Stream avec .boxed() pour les tableaux de types primitifs.
  3. Considérations particulières pour les tableaux de types primitifs, qui nécessitent un boxing explicite pour être convertis en listes d'objets wrapper.

  4. Applications pratiques et pièges courants lors de l'utilisation de tableaux et de listes, y compris la gestion de la configuration et l'interaction avec les API héritées.

Ces techniques sont précieuses dans de nombreuses applications Java réelles où vous devez travailler avec des tableaux et des listes, en particulier lorsque vous devez préserver les données d'origine tout en créant des collections flexibles et mutables pour la manipulation. Comprendre la relation entre les tableaux et les listes vous permet d'écrire du code Java plus efficace et maintenable.