Spaß mit Mutable

Ich habe neulich ein lustiges Problem in einem Programmcode gefunden. Runtergebrochen und stark vereinfacht sah das Problem so aus:

package mutable;

import java.util.HashSet;
import java.util.Set;

public class Mutable {

    static class MyEntity {

        String name;

        public MyEntity(String s) {
            name = s;
        }

        public String getName() {
            return name;
        }

        public void setName(String s) {
            name = s;
        }

        @Override
        public int hashCode() {
            return name.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final MyEntity other = (MyEntity) obj;
            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
                return false;
            }
            return true;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    public static void main(String[] args) {
        MyEntity hund = new MyEntity("Hund");
        MyEntity katze = new MyEntity("Katze");
        MyEntity maus = new MyEntity("Maus");

        Set<MyEntity> tiere = new HashSet<MyEntity>();
        tiere.add(hund);
        tiere.add(katze);
        tiere.add(maus);

        testSetFor(tiere, maus);
        System.out.println("---------------");
        maus.setName("Haus");
        testSetFor(tiere, maus);
    }

    private static void testSetFor(Set<MyEntity> set, MyEntity entry) {
        if (set.contains(entry)) {
            System.out.println("Is da!");
        } else {
            System.out.println("Nixn, kein '" + entry + "' in tiere!");
        }

        System.out.println("Ok, suche zu Fuß...");

        for (MyEntity e : set) {
            if (e.equals(entry) && e.hashCode() == entry.hashCode()) {
                System.out.print("Heureka! ");
            }
            System.out.println(e);
        }
    }
}

Was das Programm ausgibt ist folgendes:

Is da!
Ok, suche zu Fuß...
Heureka! Maus
Hund
Katze
---------------
Nixn, kein 'Haus' in tiere!
Ok, suche zu Fuß...
Heureka! Haus
Hund
Katze

Das Objekt MyEntity scheint also aus dem Set zu verschwinden, obwohl es nachweislich da ist. Die Begründung ist leicht: Der HashCode von MyEntity wird nur verwendet, wenn das Objekt in das Set eingetragen wird. Verändere ich das Objekt nachträglich und damit auch den Hashwert, kriegt das Set davon nichts mit. Es wird also innerhalb des Sets unter dem alten Hashwert referenziert.

Es ist also scheinbar keine so wahnsinnig gute Idee Objekte in einem Set zu verändern. Wenn Referenzen auf das Set quer durch alle Klassen verstreut werden, kann man nach so einem Fehler ganz schön lange suchen. 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


fünf × = 10