Categorías
Common Methods Diagram

Contrato Java equals () y hashCode ()

La superclase de Java java.lang.Object define dos métodos importantes:

public boolean equals(Object obj)
public int hashCode()

En esta publicación, primero mostraré un ejemplo de un error común y luego explicaré cómo contrato igual () y hashCode () obras.

1. Un error común

El error común se muestra en el siguiente ejemplo.

import java.util.HashMap;
 
public class Apple {
	private String color;
 
	public Apple(String color) {
		this.color = color;
	}
 
	public boolean equals(Object obj) {
		if(obj==null) return false;
		if (!(obj instanceof Apple))
			return false;	
		if (obj == this)
			return true;
		return this.color.equals(((Apple) obj).color);
	}
 
	public static void main(String[] args) {
		Apple a1 = new Apple("green");
		Apple a2 = new Apple("red");
 
		//hashMap stores apple type and its quantity
		HashMap<Apple, Integer> m = new HashMap<Apple, Integer>();
		m.put(a1, 10);
		m.put(a2, 20);
		System.out.println(m.get(new Apple("green")));
	}
}

En el método principal, se crean dos manzanas («verde» y «roja») y se colocan en un HashMap. Sin embargo, cuando se le pide al mapa que proporcione la manzana verde, no se encuentra la manzana verde. El programa anterior imprime nulo. Estamos seguros de que la manzana verde se almacena en el hashMap al inspeccionar el HashMap en el depurador.

¿Qué causa el problema?

2. Problema causado por hashCode ()

El problema es causado por el método no reemplazado «hashCode ()». El contrato entre equals () y hashCode () es:
1) Si dos objetos son iguales, entonces deben tener el mismo código hash.
2) Si dos objetos tienen el mismo código hash, pueden ser iguales o no.

La idea detrás de un mapa es poder encontrar un objeto más rápido que una búsqueda lineal. El uso de claves hash para localizar objetos es un proceso de dos pasos. Internamente, HashMap se implementa como una matriz de objetos Entry. Cada entrada tiene un par y un puntero que apunta a la siguiente entrada. El código hash del objeto clave es el índice para direccionar la matriz. Esto ubica la lista vinculada de entradas en esa celda de la matriz. La lista vinculada en la celda luego se busca linealmente usando equals () para determinar si dos objetos son iguales. ,>

La implementación predeterminada de hashCode () en la clase Object devuelve enteros distintos para diferentes objetos. Por tanto, la segunda manzana tiene un código hash diferente.

El HashMap está organizado como una secuencia de cubos. Los objetos clave se colocan en diferentes cubos. O (1) necesita tiempo para llegar al depósito correcto porque es un acceso a la matriz con el índice. Por lo tanto, es una buena práctica distribuir uniformemente los objetos en esos depósitos, es decir, tener un método hashCode () que produzca un código hash distribuido uniformemente. (Aunque no es el punto principal aquí)

La solución es agregar el método hashCode a la clase Apple. Aquí solo uso la longitud de la cadena de color para la demostración.

public int hashCode(){
	return this.color.hashCode();	
}

Categorías
Common Methods

¿Cómo genera código el compilador de Java para métodos sobrecargados y anulados?

Aquí hay un ejemplo simple de Java que muestra polimorfismo: sobrecarga y anulación.

El polimorfismo significa que las funciones asumen diferentes formas en diferentes momentos. En caso de tiempo de compilación, se denomina sobrecarga de funciones. La sobrecarga permite acceder a métodos relacionados mediante el uso de un nombre común. A veces se le llama polimorfismo ad hoc, a diferencia del polimorfismo paramétrico.

class A {
	public void M(int i){
		System.out.println("int");
	}
 
	public void M(String s){
		//this is an overloading method
		System.out.println("string");
	}
}
 
class B extends A{
	public void M(int i){
		//this is overriding method
		System.out.println("overriden int");
	}
}
public static void main(String[] args) {
	A a = new A();
	a.M(1);
	a.M("abc");
 
	A b = new B();
	b.M(1234);
}

Desde la perspectiva del compilador, ¿cómo se genera el código para las llamadas de función correctas?

La sobrecarga estática no es difícil de implementar. Al procesar la declaración de una función sobrecargada, un nuevo enlace la asigna a una implementación diferente. Durante el proceso de verificación de tipos, el compilador analiza el tipo real del parámetro para determinar qué función utilizar.

La sobrecarga dinámica permite elegir diferentes implementaciones de una función en el tipo de tiempo de ejecución de un parámetro real. Es una forma de envío dinámico.

El envío dinámico también se usa para implementar la anulación de métodos. El método anulado está determinado por el tipo de objeto real durante el tiempo de ejecución.

Para comprender el despacho dinámico, hay una publicación sobre el diseño de objetos en la memoria.

Categorías
Common Methods Versus

Comparable vs Comparador en Java

Comparable y Comparator son dos interfaces proporcionadas por Java Core API. Por sus nombres, podemos decir que pueden usarse para comparar cosas de alguna manera. Pero, ¿qué son exactamente y cuál es la diferencia entre ellos? Los siguientes son dos ejemplos para responder a esta pregunta. Los ejemplos simples comparan el tamaño de dos HDTV. Cómo usar Comparable vs Comparator es obvio después de leer el código.

1. Comparable

Comparable es implementado por una clase para poder comparar el objeto en sí mismo con algunos otros objetos. La propia clase debe implementar la interfaz para poder comparar su (s) instancia (s). El método requerido para la implementación es compareTo(). Aquí hay un ejemplo:

class HDTV implements Comparable<HDTV> {
	private int size;
	private String brand;
 
	public HDTV(int size, String brand) {
		this.size = size;
		this.brand = brand;
	}
 
	public int getSize() {
		return size;
	}
 
	public void setSize(int size) {
		this.size = size;
	}
 
	public String getBrand() {
		return brand;
	}
 
	public void setBrand(String brand) {
		this.brand = brand;
	}
 
	@Override
	public int compareTo(HDTV tv) {
 
		if (this.getSize() > tv.getSize())
			return 1;
		else if (this.getSize() < tv.getSize())
			return -1;
		else
			return 0;
	}
}
 
public class Main {
	public static void main(String[] args) {
		HDTV tv1 = new HDTV(55, "Samsung");
		HDTV tv2 = new HDTV(60, "Sony");
 
		if (tv1.compareTo(tv2) > 0) {
			System.out.println(tv1.getBrand() + " is better.");
		} else {
			System.out.println(tv2.getBrand() + " is better.");
		}
	}
}
Sony is better.

2. Comparador

En algunas situaciones, es posible que no desee cambiar una clase y hacerla comparable. En esos casos, Comparator se puede utilizar si desea comparar objetos en función de determinados atributos / campos. Por ejemplo, se pueden comparar 2 personas en función de la «altura» o la «edad», etc. (esto no se puede hacer con el método comparable).

El método requerido para implementar es comparar(). Ahora usemos otra forma de comparar esos televisores por tamaño. Un uso común de Comparator está ordenando. Ambas cosas Collections y Arrays Las clases proporcionan un método de ordenación que utiliza un Comparator.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
 
class HDTV {
	private int size;
	private String brand;
 
	public HDTV(int size, String brand) {
		this.size = size;
		this.brand = brand;
	}
 
	public int getSize() {
		return size;
	}
 
	public void setSize(int size) {
		this.size = size;
	}
 
	public String getBrand() {
		return brand;
	}
 
	public void setBrand(String brand) {
		this.brand = brand;
	}
}
 
class SizeComparator implements Comparator<HDTV> {
	@Override
	public int compare(HDTV tv1, HDTV tv2) {
		int tv1Size = tv1.getSize();
		int tv2Size = tv2.getSize();
 
		if (tv1Size > tv2Size) {
			return 1;
		} else if (tv1Size < tv2Size) {
			return -1;
		} else {
			return 0;
		}
	}
}
 
public class Main {
	public static void main(String[] args) {
		HDTV tv1 = new HDTV(55, "Samsung");
		HDTV tv2 = new HDTV(60, "Sony");
		HDTV tv3 = new HDTV(42, "Panasonic");
 
		ArrayList<HDTV> al = new ArrayList<HDTV>();
		al.add(tv1);
		al.add(tv2);
		al.add(tv3);
 
		Collections.sort(al, new SizeComparator());
		for (HDTV a : al) {
			System.out.println(a.getBrand());
		}
	}
}

Producción:

Panasonic
Samsung
Sony

A menudo podemos usar Collections.reverseOrder() método para obtener un comparador de orden descendente. Como el siguiente:

ArrayList<Integer> al = new ArrayList<Integer>();
al.add(3);
al.add(1);
al.add(2);
System.out.println(al);
Collections.sort(al);
System.out.println(al);
 
Comparator<Integer> comparator = Collections.reverseOrder();
Collections.sort(al,comparator);
System.out.println(al);

Producción:

[3,1,2]
[1,2,3]
[3,2,1]

3. ¿Cuándo usar cuál?

En resumen, una clase que implementa Comparable será comparable, lo que significa que las instancias se pueden comparar entre sí.

Una clase que implementa Comparator se utilizará principalmente en dos situaciones: 1) Se puede pasar a un método de clasificación, como Collections.sort() o Arrays.sort(), para permitir un control preciso sobre el orden de clasificación y 2) También se puede utilizar para controlar el orden de ciertas estructuras de datos, como conjuntos ordenados (p. ej. TreeSet) o mapas ordenados (p. ej., TreeMap).

Por ejemplo, para crear un TreeSet. Podemos pasar al constructor un comparador o hacer que la clase de objeto sea comparable.

Enfoque 1 – TreeSet (comparador comparador)

class Dog {
	int size;
 
	Dog(int s) {
		size = s;
	}
}
 
class SizeComparator implements Comparator<Dog> {
	@Override
	public int compare(Dog d1, Dog d2) {
		return d1.size - d2.size;
	}
}
 
public class ImpComparable {
	public static void main(String[] args) {
		TreeSet<Dog> d = new TreeSet<Dog>(new SizeComparator()); // pass comparator
		d.add(new Dog(1));
		d.add(new Dog(2));
		d.add(new Dog(1));
	}
}

Enfoque 2: Implemento comparable

class Dog implements Comparable<Dog>{
	int size;
 
	Dog(int s) {
		size = s;
	}
 
	@Override
	public int compareTo(Dog o) {
		return o.size - this.size;
	}
}
 
public class ImpComparable {
	public static void main(String[] args) {
		TreeSet<Dog> d = new TreeSet<Dog>();
		d.add(new Dog(1));
		d.add(new Dog(2));
		d.add(new Dog(1));
	}
}

Referencias:
1. Comparable
2. Comparador

Categorías
Basics Common Methods

Java hashCode () y equals () Contrato para el método de conjunto contiene (Objeto o)

El artículo trata sobre hashCode y es igual al contrato utilizado para el método contains (Object o) en Set.

Un acertijo sobre el uso del método contains () de Set

import java.util.HashSet;
 
class Dog{
	String color;
 
	public Dog(String s){
		color = s;
	}	
}
 
public class SetAndHashCode {
	public static void main(String[] args) {
		HashSet<Dog> dogSet = new HashSet<Dog>();
		dogSet.add(new Dog("white"));
		dogSet.add(new Dog("white"));
 
		System.out.println("We have " + dogSet.size() + " white dogs!");
 
		if(dogSet.contains(new Dog("white"))){
			System.out.println("We have a white dog!");
		}else{
			System.out.println("No white dog!");
		}	
	}
}

Producción:

¡Tenemos 2 perros blancos!
¡Ningún perro blanco!

Agregamos dos perros blancos al set – dogSet, y el tamaño muestra que tenemos 2 perros blancos. Pero, ¿por qué no hay un perro blanco cuando usamos el método contains ()?

El método contiene (Objeto o) de Set

Desde Java Doc, el método contains () devuelve verdadero si y solo si este conjunto contiene un elemento e tal que (o == null? E == null: o.equals (e)). Entonces, el método contains () realmente usa el método equals () para verificar la igualdad.

Tenga en cuenta que nulo se puede agregar a un conjunto como elemento. El siguiente código realmente se imprime cierto.

HashSet<Dog> a = new HashSet<Dog>();
a.add(null);
if(a.contains(null)){
	System.out.println("true");
}

La public boolean es igual a (Object obj) El método se define en la clase Object. Cada clase (incluidas las clases definidas por usted mismo) tiene Object como una superclase y es la raíz de cualquier jerarquía de clases. Todos los objetos, incluidas las matrices, implementan los métodos de esta clase.

En la clase definida por usted mismo, si no anula explícitamente este método, tendrá una implementación predeterminada. Devuelve verdadero si y solo si dos objetos se refieren al mismo objeto, es decir, x == y es verdadero.

Si cambiamos la clase Dog a la siguiente, ¿funcionará?

class Dog{
	String color;
 
	public Dog(String s){
		color = s;
	}
 
	//overridden method, has to be exactly the same like the following
	public boolean equals(Object obj) {
		if (!(obj instanceof Dog))
			return false;	
		if (obj == this)
			return true;
		return this.color.equals(((Dog) obj).color);
	}
 
}

La respuesta es no.

Ahora el problema es causado por el hashCode y es igual al contrato en Java. El método hashCode () es otro método en la clase Object.

El contrato es que si dos objetos son iguales (usando el método equals ()), deben tener el mismo hashCode (). Si dos objetos tienen el mismo código hash, es posible que no sean iguales.

La implementación predeterminada de public int hashCode () devuelve enteros distintos para objetos distintos. En este ejemplo en particular, debido a que no hemos definido nuestro propio método hashCode (), la implementación predeterminada devolverá dos enteros diferentes para dos perros blancos. Esto rompe el contrato.

Solución para el método contains () en Set

class Dog{
	String color;
 
	public Dog(String s){
		color = s;
	}
 
	//overridden method, has to be exactly the same like the following
	public boolean equals(Object obj) {
		if (!(obj instanceof Dog))
			return false;	
		if (obj == this)
			return true;
		return this.color.equals(((Dog) obj).color);
	}
 
	public int hashCode(){
		return color.length();//for simplicity reason
	}
}

Referencias:

http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html