Java është e barabartë() dhe hashCode()


Metodat Java e barabartë() dhe hashCode() janë të pranishme në klasën Object. Pra, çdo klasë java merr zbatimin e paracaktuar të barabartë() dhe hashCode(). Në këtë postim do të shqyrtojmë në detaje metodat java equals() dhe hashCode().

Java është e barabartë()

Metoda e barabartë() e definuar nga klasa e objektit si kjo:

public boolean equals(Object obj) {
        return (this == obj);
}

Sipas metodës së dokumentacionit java të barabartë(), çdo zbatim duhet t'u përmbahet parimeve të mëposhtme.

  • Për çdo objekt x, x.equals(x) duhet të kthejë true.
  • Për çdo dy objekt x dhe y, x.equals(y) duhet të kthejë true nëse dhe vetëm nëse y.equals(x) kthen true.
  • Për shumë objekte x, y dhe z, nëse x.equals(y) kthen true dhe y.equals(z) kthen true, më pas x.equals(z) duhet të kthejë true.
  • Thirrjet e shumta të x.equals(y) duhet të japin të njëjtin rezultat, përveç nëse modifikohet ndonjë nga vetitë e objektit që përdoret në zbatimin e metodës equals() .
  • Zbatimi i metodës së klasës së objektit barabartë() e kthen true vetëm kur të dyja referencat tregojnë për të njëjtin objekt.

Java hashCode()

Java Object hashCode() është një metodë vendase dhe kthen vlerën e kodit hash të numrit të plotë të objektit. Kontrata e përgjithshme e metodës hashCode() është:

  • Thirrjet e shumta të hashCode() duhet të kthejnë të njëjtën vlerë të plotë, përveç nëse modifikohet vetia e objektit që përdoret në metodën e barabartë().
  • Vlera e kodit hash të objektit mund të ndryshojë në ekzekutime të shumta të të njëjtit aplikacion.
  • Nëse dy objekte janë të barabartë sipas metodës equals(), atëherë kodi i tyre hash duhet të jetë i njëjtë.
  • Nëse dy objekte janë të pabarabarta sipas metodës equals(), nuk kërkohet që kodi i tyre hash të jetë i ndryshëm. Vlera e tyre e kodit hash mund të jetë ose jo e barabartë.

Rëndësia e metodës së barabartë() dhe hashCode().

Metodat Java hashCode() dhe equals() përdoren në zbatimet e bazuara në tabela Hash në java për ruajtjen dhe marrjen e të dhënave. E kam shpjeguar në detaje tek Si funksionon HashMap në java? Zbatimi i barabartë() dhe hashCode() duhet të ndjekë këto rregulla.

  • Nëse o1.equals(o2), atëherë o1.hashCode() == o2.hashCode() duhet të jetë gjithmonë e vërtetë.
  • Nëse o1.hashCode() == o2.hashCode është e vërtetë, kjo nuk do të thotë se o1.equals(o2) do të jetë e vërtetë.

Kur të anashkalohen metodat e barabartë() dhe hashCode()?

Kur anashkalojmë metodën e barabartë(), është pothuajse e nevojshme të anashkalojmë edhe metodën hashCode() në mënyrë që kontrata e tyre të mos shkelet nga zbatimi ynë. Vini re se programi juaj nuk do të bëjë përjashtime nëse shkelet kontrata e barabartë() dhe hashCode(), nëse nuk planifikoni të përdorni klasën si çelës tabele Hash, atëherë nuk do të krijojë asnjë problem. Nëse po planifikoni të përdorni një klasë si çelës tabele Hash, atëherë duhet të anashkaloni të dyja metodat e barabartë() dhe hashCode(). Le të shohim se çfarë ndodh kur ne mbështetemi në zbatimin e paracaktuar të metodave të barabartë() dhe hashCode() dhe përdorim një klasë të personalizuar si çelës HashMap.

package com.journaldev.java;

public class DataKey {

	private String name;
	private int id;

        // getter and setter methods

	@Override
	public String toString() {
		return "DataKey [name=" + name + ", id=" + id + "]";
	}

}
package com.journaldev.java;

import java.util.HashMap;
import java.util.Map;

public class HashingTest {

	public static void main(String[] args) {
		Map<DataKey, Integer> hm = getAllData();

		DataKey dk = new DataKey();
		dk.setId(1);
		dk.setName("Pankaj");
		System.out.println(dk.hashCode());

		Integer value = hm.get(dk);

		System.out.println(value);

	}

	private static Map<DataKey, Integer> getAllData() {
		Map<DataKey, Integer> hm = new HashMap<>();

		DataKey dk = new DataKey();
		dk.setId(1);
		dk.setName("Pankaj");
		System.out.println(dk.hashCode());

		hm.put(dk, 10);

		return hm;
	}

}

Kur të ekzekutojmë programin e mësipërm, ai do të printojë null. Kjo ndodh sepse metoda e Object hashCode() përdoret për të gjetur kovën për të kërkuar çelësin. Meqenëse nuk kemi akses te çelësat HashMap dhe po krijojmë përsëri çelësin për të rikuperuar të dhënat, do të vini re se vlerat e kodit hash të të dy objekteve janë të ndryshme dhe për këtë arsye vlera nuk gjendet.

Zbatimi i metodës së barabartë() dhe hashCode().

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + id;
	result = prime * result + ((name == null) ? 0 : name.hashCode());
	return result;
}

@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	DataKey other = (DataKey) obj;
	if (id != other.id)
		return false;
	if (name == null) {
		if (other.name != null)
			return false;
	} else if (!name.equals(other.name))
		return false;
	return true;
}

Vini re se të dyja metodat e barabartë() dhe hashCode() përdorin të njëjtat fusha për llogaritjet, në mënyrë që kontrata e tyre të mbetet e vlefshme. Nëse do të ekzekutoni përsëri programin e testimit, ne do të marrim objektin nga harta dhe programi do të printojë 10. Ne gjithashtu mund të përdorim Project Lombok për të gjeneruar automatikisht barazimet dhe zbatimin e metodës hashCode.

Çfarë është Hash Collision

Me fjalë shumë të thjeshta, zbatimet e tabelave Java Hash përdorin logjikën e mëposhtme për operacionet get dhe put.

  1. Së pari identifikoni \Kovën për t'u përdorur duke përdorur kodin hash \çelës.
  2. Nëse nuk ka objekte të pranishme në kovë me të njëjtin kod hash, atëherë shtoni objektin për funksionimin e vendosjes dhe kthejeni null për operacionin marrë.
  3. Nëse ka objekte të tjera në kovë me të njëjtin kod hash, atëherë metoda \key barabartë hyn në lojë.
    • Nëse equals() kthehet true dhe është një operacion put, atëherë vlera e objektit anashkalohet.
    • Nëse e barabartë me() kthen false dhe është një operacion put, atëherë hyrja e re shtohet në kovë.
    • Nëse equals() kthehet true dhe është një veprim marr, atëherë vlera e objektit kthehet.
    • Nëse e barabartë me() kthen false dhe është një operacion marrësh, atëherë kthehet null.

Po sikur të mos implementojmë hashCode() dhe equals()?

Ne kemi parë tashmë më lart se nëse hashCode() nuk zbatohet, ne nuk do të jemi në gjendje ta rikuperojmë vlerën sepse HashMap përdor kodin hash për të gjetur kovën për të kërkuar hyrjen. Nëse përdorim vetëm hashCode() dhe nuk implementojmë equals(), atëherë edhe vlera nuk do të merret sepse metoda e barabartë() do të kthehet false.

Praktikat më të mira për zbatimin e metodës së barabartë() dhe hashCode().

  • Përdorni të njëjtat veti në zbatimin e metodës së barabartë() dhe hashCode(), në mënyrë që kontrata e tyre të mos shkelet kur përditësohet ndonjë veçori.
  • Është më mirë të përdorim objekte të pandryshueshme si çelësin e tabelës Hash në mënyrë që të mund të ruajmë kodin hash në vend që ta llogarisim atë në çdo telefonatë. Kjo është arsyeja pse String është një kandidat i mirë për çelësin e tabelës Hash sepse është i pandryshueshëm dhe ka memorie vlerën e kodit hash.
  • Zbatoni metodën hashCode() në mënyrë që të ndodhë numri më i vogël i përplasjeve të hash-it dhe hyrjet të shpërndahen në mënyrë të barabartë në të gjitha kovat.

Ju mund ta shkarkoni kodin e plotë nga depoja jonë e GitHub.