Structures de données en Java. Méthodes de classe d'assistance utiles

Salut habr!

Je suis Ingénieur Logiciel chez EPAM. Depuis plus de 8 ans, je travaille avec du code hérité écrit en Java (en anticipant les commentaires, je note que la compréhension et la tolérance pour l'héritage ont commencé bien avant EPAM, en conclusion, vous trouverez la réponse pourquoi). Souvent, dans le travail, je suis tombé sur les mêmes défauts répétés. Cela m'a incité à écrire une note, et je veux commencer par les structures de données et les classes d'assistance Collections et Tableaux . Pour une raison quelconque, certains développeurs négligent leur utilisation, et en vain

Un développeur Java doit souvent gérer différentes structures de données. Il peut s'agir de tableaux, de toutes sortes de collections ou d'implémentations de Map . Il semblerait que tout avec eux soit clair et compréhensible, mais il y a plusieurs petites choses qui sont faciles à trébucher.

Cette note peut être utile à la fois aux débutants qui ne connaissent pas encore ces nuances et aux développeurs expérimentés qui pourraient en oublier une partie.

image
Photo de ammiel jr sur Unsplash

CHAT


Je veux faire une réservation tout de suite que ce matériel est pertinent pour Java 8. Il est clair que certaines choses ont déjà été mieux faites dans Java 9+, mais la plupart des grands projets utilisent le plus souvent la version de Java 8 (et parfois Java 6).

Quelle est la meilleure façon d'obtenir une collection basée sur un tableau?


Je suggère de commencer par la formation d'une collection basée sur un tableau.

Le plus souvent, cette méthode se produit:

Integer[] someArray = {9, 10, 11, 12}; List<Integer> list = Arrays.asList(someArray); 

Cela fonctionne certainement, mais est-ce que tout va bien? Et existe-t-il des solutions alternatives?

Deux inconvénients de cette approche viennent à l'esprit à la fois:

  • Tout d'abord, la méthode Arrays.asList renvoie une liste . Mais que faire si nous avons besoin d'une autre implémentation de Collection? Arrays.asList ne le permettra pas, mais une alternative sera envisagée un peu plus loin.
  • Deuxièmement, la liste obtenue en appelant Arrays.asList ne prend pas en charge le redimensionnement. Je pense que beaucoup ont trouvé une exception découlant de l'utilisation d'une telle liste.

À l'interface Collections , vous pouvez trouver une alternative à la méthode Arrays.asList - la méthode Collections.addAll . Voici comment l'utiliser:

 //      (List, Set, ...) Collection<Integer> collection = ...; Integer[] someArray = {9, 10, 8, 7}; Collections.addAll(collection, someArray); 

Ou tout simplement:

 Collections.addAll(collection, 11, 12, 13, 14); 

La méthode Collections.addAll accepte un objet Collection et un tableau à l'entrée. Au lieu d'un tableau, vous pouvez également spécifier des éléments séparés par des virgules.

Quels sont les avantages de Collections.addAll sur Arrays.asList ?

  • Pour commencer, lors de la création de collections basées sur le tableau Collections.addAll , cela fonctionne beaucoup plus rapidement que la méthode addAll de la collection avec l'entrée de Arrays.asList . Cela peut être trouvé dans le JavaDoc de cette méthode:
    Le comportement de cette méthode pratique est identique à celui de c.addAll (Arrays.asList (elements)), mais cette méthode est susceptible de s'exécuter beaucoup plus rapidement sous la plupart des
  • En outre, Collections.addAll fonctionne non seulement avec List , mais avec toute autre collection.
  • Et lors de l'utilisation de cette méthode, il n'y a pas de problème de redimensionnement.

Quelle est la façon la plus simple d'imprimer un tableau, un tableau multidimensionnel ou une collection?


Passons à la question de l'obtention d'une représentation imprimée d'un tableau et des collections.

Si nous créons simplement System.out.println (someArray) , nous obtenons quelque chose comme ceci:
[Ljava.lang.Integer; @ 6d06d69c.
Un résultat similaire est attendu lors de l'utilisation de la méthode toString () sur un tableau.
Pour sortir le tableau, la méthode Arrays.toString (...) vient à la rescousse .

 Integer[] someArray = new Integer[]{1, 2, 3}; System.out.println(Arrays.toString(someArray)); 

La sortie de cette ligne sera:

 [1, 2, 3] 

Si nous parlons d'un tableau multidimensionnel, vous pouvez utiliser la méthode: Arrays.deeptoString .

 int[][] a = { {1, 2, 3}, {4, 5, 6} }; System.out.println(Arrays.deepToString(a)); 

La sortie de cet extrait sera:

  [[1, 2, 3], [4, 5, 6]] 


Ainsi, il n'est pas nécessaire de trier le tableau à travers une boucle manuellement pour afficher ses éléments; il suffit d'utiliser cette méthode.

Quant aux collections ou implémentations de Map , il n'y a pas de problèmes. Toutes les structures de données à l'exception du tableau sont normalement sorties.

Supposons qu'il y ait un exemple:

 Collection<Integer> someCollection = new HashSet<>(); someCollection.add(1); someCollection.add(2); System.out.println(someCollection); Map<Integer, String> someMap = new HashMap<>(); someMap.put(1, "Some 1"); someMap.put(2, "Some 2"); System.out.println(someMap); 

Notez dans la sortie ci-dessous que l'ensemble et la carte étaient affichés dans un format facile à lire:

 [1, 2] 
 {1 = environ 1, 2 = environ 2} 


Est-il facile de comparer des tableaux entre eux?


Il existe des situations où vous devez comparer des tableaux. Il existe une méthode dans la classe Arrays qui permet une telle comparaison. La méthode Arrays.equals compare le nombre d'éléments et vérifie l'équivalence des éléments correspondants.

Disons que nous avons une classe Elements avec un champ et certains égaux

 private class Element { final String name; private Element(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Element element = (Element) o; return Objects.equals(name, element.name); } @Override public int hashCode() { return Objects.hash(name); } } 

Définissez trois tableaux:

 Element[] firstElementArray = { new Element("a"), new Element("b"), new Element("c") }; Element[] secondElementArray = {new Element("c"), new Element("b"), new Element("a") }; Element[] thirdElementArray = { new Element("a"), new Element("b"), new Element("c") }; 

Notez que les premier et troisième tableaux ont des éléments dans le même ordre.
Vous pouvez maintenant effectuer la comparaison à l'aide de la méthode Arrays.equals .

 System.out.println("first equals to second? " + Arrays.equals(firstElementArray, secondElementArray)); System.out.println("second equals to third? " + Arrays.equals(secondElementArray, thirdElementArray)); System.out.println("first equals to third? " + Arrays.equals(firstElementArray, thirdElementArray)); 

Le résultat sera le suivant:

 premier est égal au second?  faux 
 le deuxième est égal au troisième?  faux 
 premier est égal au troisième?  vrai 


Comment copier efficacement un tableau?


Souvent, vous pouvez voir dans le code copier des tableaux manuellement à l'aide de boucles. Cependant, il existe une méthode System.arraycopy qui copiera beaucoup plus rapidement.

Je suggère de regarder un exemple aussi simple:

 Element[] elements = { new Element("a"), new Element("b"), new Element("c") }; Element[] copyOfElements = new Element[elements.length]; System.arraycopy(elements, 0, copyOfElements, 0, elements.length); System.out.println(Arrays.toString(copyOfElements)); 

Nous avons un tableau d'éléments. Nous créons un tableau vide de la même longueur et copions tous les éléments du premier au second. En conséquence, nous obtenons la conclusion suivante:

 [Élément {name = 'a'}, Élément {name = 'b'}, Élément {name = 'c'}] 


Comment trier un tableau ou une collection de différentes manières?


Les tableaux peuvent être triés à l'aide de la méthode Arrays.sort (someArray) . Si vous souhaitez trier le tableau dans l'ordre inverse, vous pouvez passer Collections.reverseOrder () comme deuxième paramètre à l'entrée de cette méthode.

Par exemple, il y a un tableau que nous trions directement puis dans l'ordre inverse:

 String[] someArray = new String[]{"b", "a", "c"}; Arrays.sort(someArray); System.out.println(Arrays.toString(someArray)); Arrays.sort(someArray, Collections.reverseOrder()); System.out.println(Arrays.toString(someArray)); 

La conclusion sera la suivante:

 [a, b, c] 
 [c, b, a] 


En plus du tri direct et inverse, il arrive parfois que vous ayez besoin de trier un tableau de chaînes quel que soit le cas. Cela est facile à faire en passant String.CASE_INSENSITIVE_ORDER comme deuxième paramètre à Arrays.sort .

Collections.sort , malheureusement, ne permet de trier que les implémentations de List .

Quel algorithme trie Java?


La dernière chose à mentionner lorsque l'on parle de tri en Java est qu'en Java, le «tri simple» est utilisé pour les types les plus simples et la «fusion stable» est utilisée pour les objets. Vous ne devez donc pas dépenser de ressources pour développer votre propre implémentation de la méthode de tri jusqu'à ce que le profileur montre que cela est nécessaire.

Et si nous avons un tableau et que la méthode accepte Iterable?


Je propose maintenant de passer à une question telle que le passage d'un tableau à une méthode qui nécessite Iterable . Permettez-moi de vous rappeler que Iterable est une interface qui contient la méthode iterator () , que Iterator doit renvoyer.

S'il existe une méthode qui accepte Iterable à l'entrée, alors le tableau ne peut pas y être transféré comme ça. Bien que vous puissiez parcourir un tableau dans une boucle for , il n'est pas itérable .

 String[] someArray = new String[]{"a", "b", "c"}; for (String currentString : someArray) { ... } 

Dans cet exemple, tout va bien. Mais s'il existe une méthode:

 private static void someIteration(Iterable<String> iterable) { ... } 

Cette ligne ne compilera pas:

 someIteration(someArray); 

La seule solution dans cette situation est de convertir le tableau en une collection et de le nourrir déjà avec une telle méthode.

En bref sur quelques méthodes de collections utiles


La méthodeCommentaire
max (Collection) et max (Collection, Comparateur)
min (Collection) et min (Collection, Comparateur)
Veuillez noter que vous pouvez postuler à l'entrée du comparateur
indexOfSubList (List, List)
Recherche l'index de la première occurrence d'une liste (deuxième argument) dans une autre (premier argument)
lastIndexOfSubList (List, List)
Recherche l'index de la dernière occurrence d'une liste (deuxième argument) dans une autre (premier argument)
reverse (Liste)
Réorganise les articles dans l'ordre inverse

Qu'est-ce qui vaut la peine d'être lu?


Ce n'est qu'une petite partie des outils qui peuvent faciliter la vie du développeur lorsqu'il travaille avec des structures de données. De nombreux points intéressants dans le travail des collections elles-mêmes et des outils pratiques pour travailler avec eux se trouvent dans le livre de Bruce Eckel «Java Philosophy» (4e édition complète). Cependant, vous devez être prudent, car il rencontre des situations qui ne peuvent plus être jouées sur Java 7, Java 8 et supérieur. Bien que Java 6 soit décrit dans ce livre, son matériel reste largement pertinent aujourd'hui.

Bien sûr, la «philosophie Java» ne doit pas être limitée. La lecture de l'un de ces livres ne nuira à aucun développeur Java:

  • "Java. Programmation efficace », Joshua Bloch.
  • «Refactoring. Améliorer la conception du code existant », Martin Fowler.
  • «Code propre. Création, analyse et refactoring », Robert Martin.
  • Printemps 5 pour les professionnels, Julian Kozmin et autres.
  • «Test-Driven Java Development», Viktor Farcic, Alex Garcia (n'a pas encore été publié en russe).

Quel est le résultat?


Si vous avez trouvé des idées intéressantes qui pourraient compléter ce qui était écrit dans cet article, partagez-les dans les commentaires.

Je voudrais également souhaiter bonne chance et patience à ceux qui travaillent avec l'ancien code hérité. La plupart des grands projets sont hérités. Et leur importance pour le client est difficile à surestimer. Et le sentiment de victoire d'avoir éliminé le bug, qui a mis plus d'une semaine à trouver les raisons, n'est pas inférieur aux sentiments à la fin de la mise en œuvre d'une nouvelle fonctionnalité.

Merci de votre attention. Je serais heureux si l'une des présentations serait utile.
Tout succès!

Source: https://habr.com/ru/post/fr476098/


All Articles