Opérations sur les tableaux en Java

Opérations sur les tableaux en Java

1. Vue d'ensemble

Tout développeur Java sait qu’il n’est pas toujours facile de produire une solution propre et efficace lorsqu’il travaille avec des opérations de baie. Pourtant, ils sont un élément central de l'écosystème Java - et nous devrons les traiter à plusieurs reprises.

Pour cette raison, il est bon d’avoir un «aide-mémoire» - un résumé des procédures les plus courantes pour nous aider à résoudre le casse-tête rapidement. Ce tutoriel sera utile dans ces situations.

2. Tableaux et classes d'assistance

Avant de continuer, il est utile de comprendre ce qu'est un tableau en Java et comment l'utiliser. Si c'est la première fois que vous travaillez avec lui en Java, nous vous suggérons de jeter un œil àthis previous post où nous avons couvert tous les concepts de base.

Veuillez noter que les opérations de base prises en charge par un tableau sont, d'une certaine manière, limitées. Il n’est pas rare de voir des algorithmes complexes pour exécuter des tâches relativement simples en ce qui concerne les tableaux.

Pour cette raison, pour la plupart de nos opérations, nous utiliserons des classes et des méthodes d'assistance pour nous aider: la classeArrays fournie par Java et celle d'ApacheArrayUtils.

Pour inclure ce dernier dans notre projet, nous devrons ajouter la dépendanceApache Commons:


    org.apache.commons
    commons-lang3
    3.8.1

Nous pouvons consulter la dernière version de cet artefacton Maven Central.

3. Obtenir le premier et le dernier élément d'un tableau

C’est l’une des tâches les plus courantes et les plus simples grâce au caractère accès par index des tableaux.

Commençons par déclarer et initialiser un tableauint qui sera utilisé dans tous nos exemples (sauf indication contraire):

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

Sachant que le premier élément d'un tableau est associé à la valeur d'index 0 et qu'il a un attributlength que nous pouvons utiliser, il est alors simple de comprendre comment nous pouvons obtenir ces deux éléments:

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

4. Obtenir une valeur aléatoire d'un tableau

En utilisant l'objetjava.util.Random, nous pouvons facilement obtenir n'importe quelle valeur de notre tableau:

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

5. Ajouter un nouvel élément à un tableau

Comme nous le savons, les tableaux ont une taille fixe de valeurs. Par conséquent, nous ne pouvons pas simplement ajouter un élément et dépasser cette limite.

Nous devrons commencer par déclarer un nouveau tableau plus grand et copier les éléments du tableau de base dans le second.

Heureusement, la classeArrays fournit une méthode pratique pour répliquer les valeurs d'un tableau dans une nouvelle structure de taille différente:

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

Facultativement, si la classeArrayUtils est accessible dans notre projet, nous pouvons utiliser sesadd method  (ou son alternativeaddAll) pour atteindre notre objectif dans une instruction sur une ligne:

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

Comme nous pouvons l’imaginer, cette méthode ne modifie pas l’objet originalarray; nous devons affecter sa sortie à une nouvelle variable.

6. Insérer une valeur entre deux valeurs

En raison de son caractère indexé, l'insertion d'un élément dans un tableau entre deux autres n'est pas une tâche triviale.

Apache a considéré cela comme un scénario typique et a implémenté une méthode dans sa classeArrayUtils pour simplifier la solution:

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

Nous devons spécifier l'index dans lequel nous voulons insérer la valeur, et la sortie sera un nouveau tableau contenant un plus grand nombre d'éléments.

Le dernier argument est un argument variable (a.k.a. vararg) ainsi nous pouvons insérer n'importe quel nombre d'éléments dans le tableau.

7. Comparer deux tableaux

Même si les tableaux sontObjects et fournissent donc une méthodeequals, ils utilisent l'implémentation par défaut de celle-ci, en se basant uniquement sur l'égalité des références.

Nous pouvons de toute façon invoquer la méthodejava.util.Arraysequals pour vérifier si deux objets tableau contiennent les mêmes valeurs:

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

Remarque: cette méthode n'est pas efficace pourjagged arrays. La méthode appropriée pour vérifier l'égalité des structures multidimensionnelles est celle deArrays.deepEquals.

8. Vérifier si un tableau est vide

Il s'agit d'une affectation simple en gardant à l'esprit que nous pouvons utiliser l'attributlength des tableaux:

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

De plus, nous avons également une méthode de sécurité nulle dans la classe d'assistanceArrayUtils que nous pouvons utiliser:

boolean isEmpty = ArrayUtils.isEmpty(array);

Cette fonction dépend toujours de la longueur de la structure de données, qui considère également les valeurs nulles et les sous-tableaux vides comme des valeurs valides, nous devrons donc garder un œil sur ces cas extrêmes:

// 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. Comment mélanger les éléments d'un tableau

Afin de mélanger les éléments d'un tableau, nous pouvons utiliser la fonctionnalité deArrayUtil:

ArrayUtils.shuffle(array);

Il s'agit d'une méthodevoid et fonctionne sur les valeurs réelles du tableau.

10. Tableaux Box et Unbox

Nous rencontrons souvent des méthodes qui ne prennent en charge que les tableaux basés surObject.

Encore une fois, la classe d'assistanceArrayUtils est utile pour obtenir une version encadrée de notre tableau primitif:

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

L'opération inverse est également possible:

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

11. Supprimer les doublons d'un tableau

Le moyen le plus simple de supprimer les doublons est de convertir le tableau en une implémentationSet.

Comme nous le savons peut-être,Collections utilise des génériques et ne prend donc pas en charge les types primitifs.

Pour cette raison, si nous ne gérons pas les tableaux basés sur des objets comme dans notre exemple, nous devrons d'abord encadrer nos valeurs:

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

Remarque: nous pouvons également utiliserother techniques to convert between an array and a Set object.

De plus, si nous devons conserver l'ordre de nos éléments, nous devons utiliser une implémentation deSet différente, telle queLinkedHashSet.

12. Comment imprimer un tableau

Comme pour la méthodeequals, la fonctiontoString du tableau utilise l'implémentation par défaut fournie par la classeObject, ce qui n'est pas très utile.

Les classesArrays etArrayUtils  sont livrées avec leurs implémentations pour convertir les structures de données en unString lisible.

Outre le format légèrement différent qu'ils utilisent, la distinction la plus importante est la manière dont ils traitent les objets multidimensionnels.

La classe de Java Util fournit deux méthodes statiques que nous pouvons utiliser:

  • toString: ne fonctionne pas bien avec les tableaux irréguliers

  • deepToString: prend en charge tous les tableaux basés surObject mais ne compile pas avec des arguments de tableau primitifs

D'autre part,Apache’s implementation offers a single toString method that works correctly in any case:

String arrayAsString = ArrayUtils.toString(array);

13. Mapper un tableau sur un autre type

Il est souvent utile d’appliquer des opérations sur tous les éléments du tableau, éventuellement en les convertissant en un autre type d’objet.

Avec cet objectif à l'esprit,we’ll try to create a flexible helper method using Generics:

public static  U[] mapObjectArray(
  T[] array, Function function,
  Class 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;
}

Si nous n’utilisons pas Java 8 dans notre projet, nous pouvons ignorer l’argumentFunction et créer une méthode pour chaque mappage que nous devons effectuer.

Nous pouvons maintenant réutiliser notre méthode générique pour différentes opérations. Créons deux cas de test pour illustrer ceci:

@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);
}

Pour les types primitifs, nous devrons d'abord encadrer nos valeurs.

Comme alternative, nous pouvons nous tourner versJava 8’s Streams pour effectuer le mappage pour nous.

Nous devrons d'abord transformer le tableau en unStream deObjects. Nous pouvons le faire avec la méthodeArrays.stream.

Par exemple, si nous voulons mapper nos valeursint à une représentationString personnalisée, nous allons implémenter ceci:

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

14. Filtrer les valeurs dans un tableau

Filtrer les valeurs d'une collection est une tâche courante que nous pourrions avoir à exécuter à plusieurs reprises.

En effet, au moment où nous créons le tableau qui recevra les valeurs, nous ne pouvons pas être sûrs de sa taille finale. Par conséquent,we’ll rely on the Streams approach again.

Imaginons que nous voulions supprimer tous les nombres impairs d'un tableau:

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

16. Conclusion

Les tableaux sont l’une des fonctionnalités essentielles de Java. Il est donc très important de comprendre leur fonctionnement et de savoir ce que nous pouvons et ne pouvons pas faire avec eux.

Dans ce tutoriel, nous avons appris comment gérer les opérations sur les tableaux de manière appropriée dans des scénarios courants.

Comme toujours, le code source complet des exemples de travail est disponible surour Github repo.