miércoles, 11 de marzo de 2020

Java string literal vs string object

Java string literal vs string object

Java string literal vs string object . ¿Qué diferencia existe entre las dos opciones? . Normalmente cuando trabajamos en Java solemos crear un String de forma rápida asignando una literal . Pero al tratarse de una clase tenemos la posibilidad de crearlo también como un objeto . El hecho disponer de varias posibilidades suele generar dudas sobre cual es la mejor. Vamos a entrar un poco más a detalle sobre este tema y aclarar las cosas que son un poco peculiares. Supongamos que partimos del siguiente bloque de código.
  1. package com.arquitecturajava;
  2. public class Principal {
  3. public static void main(String[] args) {
  4. String mensaje1="hola";
  5. String mensaje2="hola2";
  6. System.out.println(mensaje1);
  7. System.out.println(mensaje2);
  8. }
  9. }
El resultado saldrá por la consola y simplemente muestra la información.
código inicial
Hasta aquí no hay ningún problema se trata de dos objetos Java y cada uno emite su propia información . Podemos construir una segunda versión del programa que generará algunas dudas razonables.
  1. package com.arquitecturajava;
  2. public class Principal {
  3. public static void main(String[] args) {
  4. String mensaje1="hola";
  5. String mensaje2="hola";
  6. System.out.println(mensaje1==mensaje2);
  7. System.out.println(mensaje1.equals(mensaje2));
  8. }
  9. }
java objetos diferentes
En este caso nos encontramos comprobando si los dos mensajes contienen el mismo contenido y si al ser dos objetos en memoria diferentes el comprobar la igualdad con == nos devuelve true o false. Todo parece bastante claro ya que son dos objetos diferentes en memoria con contenidos diferentes. Es normal que el resultado devuelva false y false.

Java string literal vs string object e igualdad

El problema real comienza cuando nosotros modificamos el código por el siguiente.
  1. package com.arquitecturajava;
  2. public class Principal {
  3. public static void main(String[] args) {
  4. String mensaje1="hola";
  5. String mensaje2="hola2";
  6. System.out.println(mensaje1==mensaje2);
  7. System.out.println(mensaje1.equals(mensaje2));
  8. }
  9. }
java igualdad
Es aquí donde las cosas nos sorprenden un poco , es fácilmente entendible que el segundo System.out devuelva true ya que los dos objetos en memoria contienen el mismo texto . Ahora bien lo que sorprende mucho es que el primer método devuelva true. ¿Cómo es esto posible? se trata de dos objetos diferentes y antes devolvía false como es que ahora simplemente cambiando el contenido de los textos el resultado es true. Para entender esto tenemos que abordar el concepto de Java String pool.

Java Pools y Strings

Cuando nosotros usamos literales a la hora de definir cadenas ocurre una cosa curiosa que Java interpreta que es una de las cadenas más típicas con las que vamos a trabajar y las ubica dentro de un pool especial de Strings para no tener que volver a construir ese objeto jamas .
java string pool
Por ejemplo es util en concatenaciones que muchas veces usan caracteres tipo “,” esa coma irá directamente al pool de strings. Por lo tanto no se genera un objeto cada vez que definimos una cadena como literal sino que se busca primero en el pool a ver si ese objeto existe.

Java string object

No sucederá lo mismo cuando nosotros construyamos un String con el operador new . En este caso siempre se creará un objeto nuevo , por lo tanto si la nueva versión del código fuera :
java string sin pool
Esto es mucho más común y es el comportamiento clásico cuando trabajamos con dos objetos diferentes.

Java String y curiosidades

La clase String soporta algunas curiosidades respecto a este tipo de funcionamiento ya que nos permite añadir al pool de Strings las cadenas que deseemos simplemente invocando a String.intern() . De esta manera si un objeto se creo como literal y por temas de rendimiento lo queremos añadir al pool Java lo hará por nosotros.
  1. package com.arquitecturajava;
  2. public class Principal {
  3. public static void main(String[] args) {
  4. String mensaje1=new String("hola");
  5. String mensaje1A=mensaje1.intern();
  6. String mensaje2="hola";
  7. System.out.println(mensaje1A==mensaje2);
  8. System.out.println(mensaje1.equals(mensaje2));
  9. }
  10. }
En este código podemos observar como obligamos con el método intern a añadir una cadena al pool y la volvemos a referenciar. Acto seguido creamos un string literal con el mismo nombre que el anterior. el resultado por la consola será que ambos apuntan al mismo objeto.
java string literal

Fuente

Java ArrayList for y sus opciones

Java ArrayList for y sus opciones

Java ArrayList for y las opciones para recorrerlos siempre vuelven locos a los principiantes y a algunos que no lo son tanto ya que cuando tenemos muchas opciones pues siempre se generan dudas a la hora de elegir. Vamos a ver cómo recorrer una lista de elementos de forma sencilla y abordar las diferentes posibilidades.

Java ArrayList for y size()

La forma más sencilla de recorrer una lista es a través de un bucle for y accediendo a la propiedad size.
java arraylist for clasico
Veámoslo en código:
  1. package com.arquitecturajava;
  2. import java.util.ArrayList;
  3. public class JavaFor {
  4. public static void main(String[] args) {
  5. ArrayList<String> lista = new ArrayList<String>();
  6. lista.add("hola");
  7. lista.add("que");
  8. lista.add("tal");
  9. lista.add("estas");
  10. lista.add("hoy");
  11. for (int i=0;i<lista.size();i++) {
  12. System.out.println(lista.get(i));
  13. }
  14. }
  15. }
El resultado se muestra en la consola:
java resultado
Cuando uno empieza esta forma parece la más clara y no parece tener mucha problemática . Sin embargo algunas veces sucede que los desarrolladores al recorrer la lista no asignan correctamente el lista.size() o ponen otro valor sobre todo cuando se es muy novato. Por lo tanto puede generarse un NullPointerException. No solo eso sino que es una forma de recorrer elementos que solo nos vale para los ArrayList.

Java for e Iteradores

Una alternativa a esta situación es usar un Iterador .Un Iterador es un interface que dispone de los métodos hasNext() y next() y nos permite recorrer una colección de elementos.
java arraylist for
Da lo mismo que sea un ArrayList que otra estructura. Por lo tanto aporta homogeneidad en las APIs.
  1. package com.arquitecturajava;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4. public class JavaFor2 {
  5. public static void main(String[] args) {
  6. ArrayList<String> lista = new ArrayList<String>();
  7. lista.add("hola");
  8. lista.add("que");
  9. lista.add("tal");
  10. lista.add("estas");
  11. lista.add("hoy");
  12. Iterator<String> it= lista.iterator();
  13. while(it.hasNext()) {
  14. System.out.println(it.next());
  15. }
  16. }
  17. }

Java 5 ArrayList for

A partir de Java 5 aparece el concepto de bucle forEach y permite simplificar la forma en la que trabajamos con los Iteradores.
  1. package com.arquitecturajava;
  2. import java.util.ArrayList;
  3. public class JavaFor3 {
  4. public static void main(String[] args) {
  5. ArrayList<String> lista = new ArrayList<String>();
  6. lista.add("hola");
  7. lista.add("que");
  8. lista.add("tal");
  9. lista.add("estas");
  10. lista.add("hoy");
  11. for (String cadena: lista) {
  12. System.out.println(cadena);
  13. }
  14. }
  15. }
Como se puede observar la forma de recorrer una lista se simplifica sobremanera comparado con el Iterador que aunque genera homogeneidad siempre era complicado de usar. Esta es una de las opciones más recomendadas hasta la llegada de Java 8.

Java 8 y Streams.

En Java 8 tenemos un salto importante a niveles de programación ya que entran todas las capacidades funcionales y con ello se pueden simplificar el manejo de listas a través de streams .
  1. package com.arquitecturajava;
  2. import java.util.ArrayList;
  3. public class JavaFor3 {
  4. public static void main(String[] args) {
  5. ArrayList<String> lista = new ArrayList<String>();
  6. lista.add("hola");
  7. lista.add("que");
  8. lista.add("tal");
  9. lista.add("estas");
  10. lista.add("hoy");
  11. lista.forEach(System.out::println);
  12. }
  13. }
La simplificación es clara aun así nos quedan situaciones clásicas en las que un bucle de forEach de Java 5 nos aporta más ya que por ejemplo deseamos cambiar el contenido de los elementos del Array y luego imprimirlo.
  1. package com.arquitecturajava;
  2. import java.util.ArrayList;
  3. public class JavaFor6 {
  4. public static void main(String[] args) {
  5. ArrayList<String> lista = new ArrayList<String>();
  6. lista.add("hola");
  7. lista.add("que");
  8. lista.add("tal");
  9. lista.add("estas");
  10. lista.add("hoy");
  11. for (int i = 0; i < lista.size(); i++) {
  12. lista.set(i, lista.get(i).toUpperCase());
  13. }
  14. for (String cadena : lista) {
  15. System.out.println(cadena);
  16. }
  17. }
  18. }
Este bloque nos recorrera el array y volverá a recorrerlo de una forma clásica para mostrar la información en pantalla actualizada.
mayusculas
¿Existe una forma de realizar esta operación de una forma más compacta en Java 8 ? . La realidad es que sí . Podemos usar el método replaceAll del ArrayList que recibe un interface funcional y permite actualizar todos los elementos de golpe a continuación usaremos el método forEach.
  1. package com.arquitecturajava;
  2. import java.util.ArrayList;
  3. public class JavaFor5 {
  4. public static void main(String[] args) {
  5. ArrayList<String> lista = new ArrayList<String>();
  6. lista.add("hola");
  7. lista.add("que");
  8. lista.add("tal");
  9. lista.add("estas");
  10. lista.add("hoy");
  11. lista.replaceAll(String::toUpperCase);
  12. lista.forEach(System.out::println);
  13. }
  14. }
De esta forma el resultado será idéntico pero con mucho menos código.

Fuente: Arquitectura Java