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.
Photo de ammiel jr sur UnsplashCHAT
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:
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
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!