Collections HashSet verständnisproblem

Marc T.

Bekanntes Mitglied
Guten Tag,

ich war bisher der Annahme, dass beim Aufruf der Methode
Java:
java.util.HashSet.add(Object obj)
Der Hashwert des objekts mittels der Methode
Java:
public int hashCode()
ermittelt und mit den bereits vorhandenen werten des HashSets verglichen wird.

Ich habe hierzu folgende Testklasse implementiert:

Java:
public class Test {
	/**
	 * 
	 */
	private Set<String> words = null;
	
	/**
	 * 
	 * @param word
	 */
	public Test(Set<String> words) {
		this.words = words;
	}

	@Override
	public int hashCode() {
		int hash = 5;
		
		hash = 89 * hash + this.words.hashCode();
		
		return hash;
	}

	@Override
	public String toString() {
		String result = "";
		
		result = result + words.toString();
		
		return result;
	}
}

Jetzt möchte ich zwei Instanzen der Klasse Test erzeugen und in
ein HashSet packen. Ich sorge dafür, dass beide Klassen die gleichen
Elemente in ihrer Collection stehen haben, der entstehende hashcode
also gleich sein müsste:

Java:
public class TestMain {

	public static void main(String[] args) {
		Set<String> temp = new HashSet<String>();
		
		temp.add("Das");
		temp.add("ist");
		temp.add("ein");
		temp.add("Test");
		
		Test test1 = new Test(temp);
		Test test2 = new Test(temp);

		System.out.println("Hashwert: " + test1.hashCode());
		System.out.println("Hashwert: " + test2.hashCode());

		Set<Test> testSet = new HashSet<Test>();
		
		testSet.add(test1);
		testSet.add(test2);
		
		for(Test test : testSet) {
			System.out.println(test);
		}
	}
}

Nun habe ich allerdings in meinem Set zwei mal das Test-Objekt
mit ein und dem selben hashcode drin stecken. Wo ist hier mein
Denkfehler bzw. wie sorge ich dafür, dass ein Objekt mit dem gleichen
Hashwert nicht nochmal in mein Set hinzugefügt wird?

Code:
Hashwert: 2877113
Hashwert: 2877113
[Test, Das, ein, ist]
[Test, Das, ein, ist]
 
Moin,

Nun habe ich allerdings in meinem Set zwei mal das Test-Objekt mit ein und dem selben hashcode drin stecken. Wo ist hier mein Denkfehler bzw. wie sorge ich dafür, dass ein Objekt mit dem gleichen
Hashwert nicht nochmal in mein Set hinzugefügt wird?
Ich verstehe jetzt die Frage nicht wirklich ... der Code macht doch genau das was er soll :noe:
Du erzeugst ZWEI Objekte, gibst ZWEI Strings aus und fügst BEIDE dem hashSet hinzu ....

Gruß
Klaus
 
Ich glaube es geht ihm eher darum das beide Objekte einen Identischen Hash haben , was aber klar ist da beide Objekte in dieser Methode

Java:
@Override
    public int hashCode() {
        int hash = 5;
        
        hash = 89 * hash + this.words.hashCode();
        
        return hash;
    }

eben einen Identischen Hash berechnen, der die Speicheradresse nicht mit einbezieht, wenn sich nun die variable word für jedes Objekt ändern würde würde auch ein unterschiedlicher Hashwert berechnet werden.
 
Zuletzt bearbeitet:
Nein nein, das ist nicht das was ich meine. Ich möchte nicht,
dass das Objekt ein zweites mal dem Set hinzugefügt wird,
wenn der Hashwert der gleiche ist. Ich dachte das wäre der
Sinn eines HashSets, dass jedes Objekt, identifiziert durch seinen
hashwert, nur genau einmal im Set vorhanden sein darf.
 
Zuletzt bearbeitet:
Ich möchte nicht,
dass das Objekt ein zweites mal dem Set hinzugefügt wird,

Das ist der Sinn des HashSet's 🙂

Schau dir mal die implementierung dahinter an wenn du Objekte zum Set hinzufügst, das Set verwendet eine HashMap für dessen Datenspeicherung , du musst also eigentlich wenn du die
Code:
hashCode()
überschreibst auch
Code:
equals()
überschieben

Java:
private static final Object PRESENT = new Object();

 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

Du überschreibst zwar hashCode , dieser wird aber in der Standardimplementierung von equals(); nicht mit einbezogen!
 
Zuletzt bearbeitet:
Was ergibt wird den ausgegeben wenn du deine Testobjekte mal mit
Code:
==
vergleichts, ich vermute mal false oder ?

Hier nochmal im Detail :

Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.

Specified by: add(...) in Set, Overrides: add(...) in AbstractCollection
Parameters:
e element to be added to this set
Returns:
true if this set did not already contain the specified element

Das hier ist relevant :

Java:
(e==null ? e2==null : e.equals(e2))
 
Zuletzt bearbeitet:
Achso, also ist es dem HashSet völlig egal was im hashcode() steht.
Die interessante Methode ist equals. Okay. Dann pack ich in die equals
einfach rein ob der hashcode gleich ist dann müsste ich das haben was
ich möchte.
 
Hier mal ein Beispiel

Java:
public class Foo{
	
	public Foo() {}
	
	class Test{
		String s;
		
		public Test(String s){
			this.s = s;
		}
		
		@Override
		public int hashCode(){
			return 13;
		}
		
		@Override
		public boolean equals(Object o){
			if (this == o) {
	            return true;
            }
			return false;
		}
		
		public String getString() 			{return this.s;}
		
	}
	
	public static void main(String[] args) {
	 
		HashSet<Object> set = new HashSet<>();
		
		Test one = new Foo().new Test("Hallo");
		Test two = new Foo().new Test("Hallo");
		
		System.out.println(one.hashCode());
		System.out.println(two.hashCode());
		
		System.out.println(one.equals(two));
		System.out.println(one == two);
		System.out.println(one.getString().equals(two.getString()));
		
		System.out.println(set.add(one));  // wird hinzugefügt !
		
		
		System.out.println(set.add(one));  // wird NICHT hinzugefügt weil bereits vorhanden !
		System.out.println(set.size());

    }
}

Ausgabe ist :

13
13
false
false
true
false
1

Wie du deutlich sehen kannst sind Objekt one und two obwohl sie den gleichen Hashwert besitzen nicht identisch, hier ist wichtig zu wissen das gleich und identisch verschiedene definitionen besitzen,

nur weil zwei Objekte identisch sind , sind diese noch lange nicht Gleich !

die Methode set(Objekt o) liefert einen boolschen wert, der dafür steht ob ein Objekt hinzugefügt wurde oder nicht

in unserem Fall versuchen wir 2 mal das gleiche Objekt hinzuzufügen, bedeutet der die erste ausgabe liefert true , das Objekt wird hinzugefügt und die zweite liefert false, da dieses Objekt bereits im Set vorhanden ist

Obwohl nun Objekt one und Objekt two inhaltlich identisch sind, sind es dennoch verschiedene Objekte, ein Referenzvergleich mittels
Code:
==
liefert demnach hierfür false!

anders wenn wir nun folgendes machen würden :

Java:
Test one = new Foo().new Test("Hallo");
Test two = one;

nun Referenziert Objekt two Objekt one , verweist also auf das gleiche Objekt und somit auch auf die gleiche Speicheradressierung, demnach liefert jetzt ein Referenzvergleich mittels
Code:
==
nun
Code:
true
.

Hoffe es war halbwegs übersichtlich , wenn du noch Fragen haben solltest immer raus damit 🙂
 
Zuletzt bearbeitet:
Ja, um die von mir gewünschte Funktionalität zu bekommen also einfach:

Java:
public class Test {
    /**
     * 
     */
    private Set<String> words = null;
    
    /**
     * 
     * @param word
     */
    public Test(Set<String> words) {
        this.words = words;
    }
	
	@Override
	public boolean equals(Object o) {
		return this.hashCode() == o.hashCode();
	}
 
    @Override
    public int hashCode() {
        int hash = 5;
        
        hash = 89 * hash + this.words.hashCode();
        
        return hash;
    }
 
    @Override
    public String toString() {
        String result = "";
        
        result = result + words.toString();
        
        return result;
    }
}

Und es funktioniert. Danke.
 

Zurück
Oben