Estructuras de datos en Java. Métodos útiles de clase auxiliar

Hola habr!

Soy ingeniero de software en EPAM. Durante más de 8 años he estado trabajando con código heredado escrito en Java (anticipándome a los comentarios, noto que la comprensión y la tolerancia al legado comenzaron mucho antes de EPAM, en conclusión encontrarás la respuesta por qué). A menudo en el trabajo me encontré con los mismos defectos repetidos. Esto me llevó a escribir una nota, y quiero comenzar con las estructuras de datos y las clases auxiliares Colecciones y matrices . Por alguna razón, algunos desarrolladores descuidan su uso, y en vano

Un desarrollador de Java a menudo tiene que lidiar con varias estructuras de datos. Pueden ser matrices, todo tipo de colecciones o implementaciones de Map . Parece que todo con ellos es claro y comprensible, pero hay varias pequeñas cosas con las que es fácil tropezar.

Esta nota puede ser útil tanto para principiantes que aún no conocen estos matices, como para desarrolladores experimentados que podrían olvidar algo de esto.

imagen
Foto de ammiel jr en Unsplash

CAT


Quiero hacer una reserva de inmediato de que este material es relevante para Java 8. Está claro que algunas cosas ya se han hecho mejor en Java 9+, pero la mayoría de los proyectos grandes a menudo usan la versión de Java 8 (y a veces Java 6).

¿Cuál es la mejor manera de obtener una colección basada en matriz?


Sugiero comenzar con la formación de una colección basada en una matriz.

Muy a menudo, este método ocurre:

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

Ciertamente funciona, pero ¿está todo bien? ¿Y hay alguna solución alternativa?

Me vienen a la mente dos desventajas de este enfoque:

  • Primero, el método Arrays.asList devuelve una Lista . Pero, ¿qué pasa si necesitamos otra implementación de Collection? Arrays.asList no permitirá esto, pero se considerará un poco más una alternativa.
  • En segundo lugar, la Lista obtenida llamando a Arrays.asList no admite el cambio de tamaño. Creo que muchos han llegado con una excepción derivada de trabajar con dicha lista.

En la interfaz de Colecciones , puede encontrar una alternativa al método Arrays.asList : el método Collections.addAll . Aquí se explica cómo usarlo:

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

O simplemente:

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

El método Collections.addAll acepta un objeto Collection y una matriz en la entrada. En lugar de una matriz, también puede especificar elementos separados por comas.

¿Cuáles son los beneficios de Collections.addAll sobre Arrays.asList ?

  • Para comenzar, al crear colecciones basadas en la matriz Collections.addAll , funciona mucho más rápido que el método addAll de la colección con la entrada de Arrays.asList . Esto se puede encontrar en el JavaDoc de este método:
    El comportamiento de este método de conveniencia es idéntico al de c.addAll (Arrays.asList (elementos)), pero es probable que este método se ejecute significativamente más rápido en la mayoría
  • Además, Collections.addAll funciona no solo con List , sino con cualquier otra colección.
  • Y cuando se utiliza este método, no hay problema de cambio de tamaño.

¿Cuál es la forma más fácil de imprimir una matriz, matriz multidimensional o colección?


Pasemos al tema de obtener una representación impresa de una matriz y colecciones.

Si solo hacemos System.out.println (someArray) , obtenemos algo como esto:
[Ljava.lang.Integer; @ 6d06d69c.
Se espera un resultado similar cuando se usa el método toString () en una matriz.
Para generar la matriz, el método Arrays.toString (...) viene al rescate .

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

El resultado para esta línea será:

 [1, 2, 3] 

Si estamos hablando de una matriz multidimensional, puede usar el método: Arrays.deeptoString .

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

El resultado de este fragmento será:

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


Por lo tanto, no es necesario ordenar la matriz a través de cualquier bucle manualmente para mostrar sus elementos; es suficiente usar este método.

En cuanto a las colecciones o implementaciones de Map , no hay problemas. Todas las estructuras de datos, excepto la matriz, se generan normalmente.

Supongamos que hay un ejemplo:

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

Tenga en cuenta en el resultado a continuación que tanto el conjunto como el Mapa se muestran en un formato fácil de leer:

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


¿Qué tan fácil es comparar matrices entre sí?


Hay situaciones en las que necesita comparar matrices. Hay un método en la clase Arrays que permite tal comparación. El método Arrays.equals compara el número de elementos y verifica la equivalencia de los elementos correspondientes.

Digamos que tenemos una clase de Elementos con un campo y ciertos iguales

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

Defina tres matrices:

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

Tenga en cuenta que las matrices primera y tercera tienen elementos en el mismo orden.
Ahora puede realizar la comparación utilizando el método 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)); 

El resultado será el siguiente:

 primero es igual a segundo?  falso 
 segundo igual a tercero?  falso 
 primero es igual a tercero?  cierto 


¿Cómo copiar eficientemente una matriz?


A menudo puede ver en el código copiando manualmente matrices usando bucles. Sin embargo, hay un método System.arraycopy que copiará mucho más rápido.

Sugiero echar un vistazo a un ejemplo tan 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)); 

Tenemos una gran variedad de elementos. Creamos una matriz vacía de la misma longitud y copiamos todos los elementos del primero al segundo. Como resultado, obtenemos la siguiente conclusión:

 [Elemento {nombre = 'a'}, Elemento {nombre = 'b'}, Elemento {nombre = 'c'}] 


¿Cómo ordenar una matriz o colección de diferentes maneras?


Las matrices se pueden ordenar utilizando el método Arrays.sort (someArray) . Si desea ordenar la matriz en orden inverso, puede pasar Collections.reverseOrder () como el segundo parámetro a la entrada de este método.

Por ejemplo, hay una matriz que clasificamos en orden directo y luego en orden inverso:

 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 conclusión será la siguiente:

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


Además de la ordenación directa e inversa, a veces sucede que necesita ordenar una serie de cadenas independientemente del caso. Esto es fácil de hacer pasando String.CASE_INSENSITIVE_ORDER como el segundo parámetro a Arrays.sort .

Collections.sort , desafortunadamente, solo permite ordenar las implementaciones de la Lista .

¿Qué algoritmo clasifica Java?


Lo último que se debe mencionar al hablar sobre la ordenación en Java es que en Java, la "ordenación simple" se usa para los tipos más simples y la "fusión estable" para los objetos. Por lo tanto, no debe gastar recursos en desarrollar su propia implementación del método de clasificación hasta que el generador de perfiles muestre que es necesario.

¿Qué pasa si tenemos una matriz y el método acepta Iterable?


Propongo ahora pasar a una pregunta como pasar una matriz a un método que requiere Iterable . Permítame recordarle que Iterable es una interfaz que contiene el método iterator () , que Iterator debería devolver.

Si hay un método que acepta Iterable en la entrada, entonces la matriz no puede transferirse allí así como así. Aunque puede iterar sobre una matriz en un bucle for , no es Iterable .

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

En este ejemplo, todo está bien. Pero si hay un método:

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

Esa línea no compilará:

 someIteration(someArray); 

La única salida en esta situación es convertir la matriz en una colección y ya alimentarla a dicho método.

Brevemente sobre algunos métodos útiles de Colecciones


MétodoComentario
max (Colección) y max (Colección, Comparador)
min (Colección) y min (Colección, Comparador)
Tenga en cuenta que puede aplicar a la entrada de Comparator
indexOfSubList (Lista, Lista)
Encuentra el índice de la primera aparición de una lista (segundo argumento) en otra (primer argumento)
lastIndexOfSubList (Lista, Lista)
Encuentra el índice de la última aparición de una lista (segundo argumento) en otra (primer argumento)
reverso (Lista)
Reordena los artículos en orden inverso

¿Qué vale la pena leer?


Esta es solo una pequeña parte de las herramientas que pueden facilitarle la vida al desarrollador cuando trabaja con estructuras de datos. En el libro de Bruce Eckel "Filosofía de Java" (4ª edición completa) se pueden encontrar muchos puntos interesantes en el trabajo de las colecciones mismas y herramientas convenientes para trabajar con ellos. Sin embargo, debe tener cuidado, ya que se encuentra con situaciones que ya no se pueden reproducir en Java 7, Java 8 y versiones posteriores. Aunque Java 6 se describe en este libro, su material sigue siendo relevante en la actualidad.

Por supuesto, la "filosofía de Java" no debe ser limitada. Leer cualquiera de estos libros no dañará a ningún desarrollador de Java:

  • "Java. Programación efectiva ", Joshua Bloch.
  • “Refactorizando. Mejora del diseño del código existente ", Martin Fowler.
  • “Código limpio. Creación, análisis y refactorización ”, Robert Martin.
  • Primavera 5 para profesionales, Julian Kozmin y otros.
  • "Desarrollo Java basado en pruebas", Viktor Farcic, Alex García (aún no se ha publicado en ruso).

Cual es el resultado?


Si se le ocurrieron ideas interesantes que podrían complementar lo que se escribió en este artículo, compártalos en los comentarios.

También me gustaría desear buena suerte y paciencia a quienes trabajan con el antiguo código heredado. La mayoría de los proyectos importantes son heredados. Y su importancia para el cliente es difícil de sobreestimar. Y la sensación de victoria al eliminar el error, que tardó más de una semana en encontrar los motivos, no es inferior a los sentimientos al final de la implementación de una nueva característica.

Gracias por su atencion Me alegraría si alguno de los presentados sería útil.
Todo el exito!

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


All Articles