Avatar billede jespersahner Nybegynder
12. april 2005 - 13:54 Der er 25 kommentarer og
1 løsning

HashMap og containsKey()

Jeg bruger en HashMap til at gemme nogle informationer, hvor min key er et selvdefineret objekt, her kaldet Objekt (og ikke en String, som man måske tit vil bruge). Objekt-klassen overskriver equals()-metoden og returnerer i test-eksemplet 'true'.

Eks.:

import java.util.*;

public class Test {   
    public static void main(String[] args) throws Exception {
        HashMap hm=new HashMap();
        Objekt x=new Object(1,2,3);
        Objekt y=new Object(1,2,3);
        System.out.println(x.equals(y));
        hm.put(x,"key");
        System.out.println(hm.containsKey(y));
    }   
}

Jeg får flg. output:
true
false

Men hvorfor får jeg her 'false' i forbindelse med containsKey(), når equals() returnerer 'true'?
Avatar billede fsconsult.dk Nybegynder
12. april 2005 - 13:58 #1
vil gætte på at du skal implementere compare metoden
Avatar billede erikjacobsen Ekspert
12. april 2005 - 13:59 #2
Har du overskrevet hashCode funktionen?
Avatar billede jespersahner Nybegynder
12. april 2005 - 14:00 #3
->erikjacobsen: Nope.
Avatar billede erikjacobsen Ekspert
12. april 2005 - 14:01 #4
Det ville nok være en god idé ;)
Avatar billede snoop_one Nybegynder
12. april 2005 - 14:03 #5
dvs. at du har både en object klasse samt en objekt klasse?

Under alle omstændigheder skal du også huske at overskrive hashcode:
"Note that it is generally necessary to override the hashCode  method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes."
Avatar billede jespersahner Nybegynder
12. april 2005 - 15:57 #6
->erikjakobsen: Det ser ud til at virke, når jeg bare overskriver hashCode()-metoden og returnerer en dummy-værdi. Smid lige et svar.
Avatar billede simonvalter Praktikant
12. april 2005 - 16:10 #7
en dummy værdi?
du bruger vel ikke dette >

-----------------------
// The worst possible legal hash function - never use!
public int hashCode() { return 42; }
It's legal because it ensures that equal objects have the same hash code. It's atrocious because
it ensures that every object has the same hash code. Therefore every object hashes to the same
bucket, and hash tables degenerate to linked lists. Programs that should run in linear time run
instead in quadratic time. For large hash tables, this is the difference between working and not
working.
Effective Java: Programming Language Guide
33
A good hash function tends to produce unequal hash codes for unequal objects. This is exactly
what is meant by the third provision of the hashCode contract. Ideally, a hash function should
distribute any reasonable collection of unequal instances uniformly across all possible hash
values.
---------------------------

kontrakten du skal overholde er beskrevet her >

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#hashCode()

lidt kode der gør det nemmere kan ses her

http://www.javapractices.com/Topic28.cjp

du plejer at gå op i performance så du burde overveje at implementere hashcode ordenligt :)
Avatar billede simonvalter Praktikant
12. april 2005 - 16:12 #8
det og meget mere er beskrevet i effective java, Joshua Bloch
http://java.sun.com/docs/books/effective/
Avatar billede erikjacobsen Ekspert
12. april 2005 - 19:02 #9
Jeg samler ikke på point, tak.

Hvis du har de 3 heltals-værdier i din klasse, og du tester dem for lighed, kan du lave en hashfunktion i retning af:    10000*a+100*b+c;    Tænk lidt over hvilke værdier de kan antage, og vælge konstanterne derefter
Avatar billede jespersahner Nybegynder
13. april 2005 - 13:51 #10
->simonvalter: Tak for det fine input! Jeg har faktisk Joshua Blochs bog, men jeg har ikke studeret denne del :-)

Problemet jeg løber ind i konkret, hvis jeg ikke overskriver hashCode(), er vel (http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#hashCode()):
"If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result."

Den umiddelbart nemme løsning er derfor blot at overskrive med en (den samme) dummy-værdi, men det er så måske ikke så snedigt alligevel, jf. din kommentar.

Her må man så ud i noget i retning af, hvad erikjacobsen skriver, men det kan hurtigt blive lidt kringlet, hvis det objekt, som man udfører equals() på, indeholder såvel tekst som tal, hvad sagen er i mit tilfælde.
Avatar billede jespersahner Nybegynder
13. april 2005 - 13:53 #11
Man kunne måske se lidt på, hvad String-klassen gør i relation til hashCode() tænker jeg..
Avatar billede simonvalter Praktikant
13. april 2005 - 13:55 #12
Nu ved jeg ikke hvilken IDE du bruger men intellij har en indbygget funktion til at generere equals/hashcode så det er klaret på 5 sec. Eclipse må have det samme.. eller ihvertfald et plugin til det.
Avatar billede erikjacobsen Ekspert
13. april 2005 - 13:58 #13
Og kan du ikke automatisk er det med strings nu meget nemt: De har selv en hashCode, og den/dem tager du bare og lægger til din egen.
Avatar billede simonvalter Praktikant
13. april 2005 - 14:00 #14
her er hvad intellij gør for et test program

class ProgramNode {

private IdentifierNode idNode;
private int test;
private long test2;
private double test3;
private String bleh;
   

        public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProgramNode)) return false;

        final ProgramNode programNode = (ProgramNode) o;

        if (test != programNode.test) return false;
        if (test2 != programNode.test2) return false;
        if (test3 != programNode.test3) return false;
        if (bleh != null ? !bleh.equals(programNode.bleh) : programNode.bleh != null) return false;
        if (idNode != null ? !idNode.equals(programNode.idNode) : programNode.idNode != null) return false;

        return true;
    }

    public int hashCode() {
        int result;
        long temp;
        result = (idNode != null ? idNode.hashCode() : 0);
        result = 29 * result + test;
        result = 29 * result + (int) (test2 ^ (test2 >>> 32));
        temp = test3 != +0.0d ? Double.doubleToLongBits(test3) : 0l;
        result = 29 * result + (int) (temp ^ (temp >>> 32));
        result = 29 * result + (bleh != null ? bleh.hashCode() : 0);
        return result;
    }

}
Avatar billede jespersahner Nybegynder
13. april 2005 - 14:03 #15
->simonvalter: Jeg bruger netBeans.
->erikjacobsen: Er det så nemt? Hvis et objekt indeholder to variabler a og b, hvor a er en String, og b er en integer, hvordan vil du så definere hasCode() entydigt?
Avatar billede erikjacobsen Ekspert
13. april 2005 - 14:08 #16
hashCode skal ikke defineres entydigt. Der skal bare være en rimelig god spredning på værdierne. I dit tilfælde: hashCode fra strengen + tallet.
Avatar billede erikjacobsen Ekspert
13. april 2005 - 14:11 #17
Og som du kan se i eksemplet fra Simon: man tager ikke hashCode på en null-streng, så du skal lige teste ;)
Avatar billede jespersahner Nybegynder
13. april 2005 - 14:12 #18
Tænker lige højt..

Entydigheden er nok ligegyldig her. Altså det gør ikke noget at to forskellige (mht. equals()-metoden) objekter i visse tilfælde producerer samme hashCode(), blot hashCode() er den samme, hvis objekterne ens mht. equals().

Derfor kan man bare definere x=a.hashCode()+b, idet x er den samme for samme to objekter med samme værdier for a og b. Omvendt er x ikke entydig, men det gør ikke noget.
Avatar billede jespersahner Nybegynder
13. april 2005 - 14:14 #19
->erikjacobsen: Ja, så er vi enige.
Avatar billede snoop_one Nybegynder
13. april 2005 - 14:15 #20
Her her et lille eksempel:

public class Test {
    private String string;
    private int integerOne;
    private int integerTwo;
    public Test(String string, int integerOne, int integerTwo){
        this.string = string;
        this.integerOne = integerOne;
        this.integerTwo = integerTwo;
    }
   
    public boolean equals(Object o){
        if(!(o instanceof Test)){
            throw new IllegalArgumentException("The argument must be of type Test");
        }
        Test test = (Test) o;
        //eqals køre kun på string og integerOne
        return (string.equals(test.string) && integerOne == test.integerOne);
    }
   
    public int hashCode(){
        //eftersom equals kun køre på string og integerOne køre hashcode på samme
        return new Integer(integerOne).hashCode() + string.hashCode();
    }
   
    public static void main(String... args){
        ArrayList<Test> list = new ArrayList<Test>();
        list.add(new Test("Test1",1,2));
        list.add(new Test("Test2",2,3));
        System.out.println(list.contains(new Test("Test1",1,1)));
    }
}
Avatar billede snoop_one Nybegynder
13. april 2005 - 14:17 #21
Håber, at du kan følge tankegangen ved bare at bruge dine attributters hashcode til at lave din egen hashcode med iht. din equals metode.
Avatar billede erikjacobsen Ekspert
13. april 2005 - 14:20 #22
new Integer(integerOne).hashCode()  giver vist bare værdien af integerOne  -  det er overflødigt at lave et objekt bare for det ;)
Avatar billede erikjacobsen Ekspert
13. april 2005 - 14:21 #23
Prøv selv:

System.out.println((new Integer(7)).hashCode());
Avatar billede snoop_one Nybegynder
13. april 2005 - 14:23 #24
jep - men bare for at illustrere at alle java's objekter implementere deres egne hashcode methode man kan bruge til at opbygge sin egen hashkode...

Men du har fuldstændig ret...
Avatar billede jespersahner Nybegynder
13. april 2005 - 14:26 #25
Ingen der vil have point?
Avatar billede snoop_one Nybegynder
13. april 2005 - 14:27 #26
;-)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester