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.
Foto de ammiel jr en UnsplashCAT
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:
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
¿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!