Java Forum / General / July 2007
HashTable
George - 17 Jul 2007 13:33 GMT Dear All,
I am using a HashTable with HashSets as the keys (they point to integers and it has to be that way round because of an algorithm I am implementing).
I calculate the HashSet from a different method and then I use hashtablename.containsKey(hashset). The problem is that altough the values of the hashset are identical containsKey will fail.
Any ideas as to how I might get the correct results?
George
Gordon Beaton - 17 Jul 2007 13:42 GMT > I am using a HashTable with HashSets as the keys (they point to > integers and it has to be that way round because of an algorithm I [quoted text clipped - 5 lines] > > Any ideas as to how I might get the correct results? Have you modified the HashSet in any way (added or removed elements) after storing it in the HashTable?
Have you confirmed that the HashSet in the HashTable and the object you use to find it with, in fact have the same hashCode()? Are they equals()?
What kind of objects are stored in the HashSet, and did you remember to implement hashCode() and equals() methods properly in *those* objects?
/gordon
--
George - 17 Jul 2007 13:49 GMT On Tue, 17 Jul 2007 Lekeas GK wrote...
|From: Gordon Beaton <n.o.t@for.email> |Date: 17 Jul 2007 12:42:37 GMT [quoted text clipped - 12 lines] |Have you modified the HashSet in any way (added or removed elements) |after storing it in the HashTable? No, they have not been modified at all.
|Have you confirmed that the HashSet in the HashTable and the object |you use to find it with, in fact have the same hashCode()? Are they |equals()? They contain two objects each, which are exactly the same. Does that not make equal?
|What kind of objects are stored in the HashSet, and did you remember |to implement hashCode() and equals() methods properly in *those* |objects? HashSet stores SigElement objects (custom datatype but simple - a couple of string objects really). But would they not get these methods from the Object class?
|/gordon | |-- Lew - 17 Jul 2007 13:54 GMT Gordon Beaton:
> |Have you confirmed that the HashSet in the HashTable and the object > |you use to find it with, in fact have the same hashCode()? Are they > |equals()?
> They contain two objects each, which are exactly the same. Does that not > make equal? Yes, that does /not/ make the HashSets equal.
What is the definition of HashSet.equals()?
 Signature Lew
Gordon Beaton - 17 Jul 2007 14:12 GMT > HashSet stores SigElement objects (custom datatype but simple - a > couple of string objects really). But would they not get these > methods from the Object class? They do unless you've overridden the methods, which you should always do when you store your own object types in any collection that uses hashCode() and equals() on its contents. The default methods don't work in most realistic situations.
Here's a test: create two distinct SigElements objects with the same contents. Display their hashcodes and the results of s1.equals(s2). Do they appear to be equivalent?
/gordon
--
Lew - 17 Jul 2007 13:53 GMT George wrote:
>> I am using a HashTable with HashSets as the keys (they point to >> integers and it has to be that way round because of an algorithm I [quoted text clipped - 5 lines] >> >> Any ideas as to how I might get the correct results?
> Have you modified the HashSet in any way (added or removed elements) > after storing it in the HashTable? > > Have you confirmed that the HashSet in the HashTable and the object > you use to find it with, in fact have the same hashCode()? Assuming java.util.HashSet, the hashCode() calculation is completely reliable.
> Are they equals()? Ay, there's the rub. They aren't.
> What kind of objects are stored in the HashSet, and did you remember > to implement hashCode() and equals() methods properly in *those* > objects? Irrelevant. He's comparing HashSets, not their contents.
It's hard to be sure without an SSCCE, but a couple of things might be wrong:
First of all, who provides the HashTable class? Maybe there is something strange about it. (Unless you meant java.utilHashtable, but your spelling says otherwise. If you did mean Hashtable, why are you using that Map implementation? Is synchronization needed?)
Second, what is the definition of HashSet.equals()? As it happens, you're guaranteeing that the HashSet in the HashTable (sic) is different from the one you're querying. That's why the lookup is failing.
 Signature Lew
Gordon Beaton - 17 Jul 2007 14:01 GMT > Irrelevant. He's comparing HashSets, not their contents. The hashcode of the HashSet is based on the hashcodes of the elements it contains. A second HashSet containing supposedly identical objects, but whose hashcodes are different, will itself have a different HashCode. How can Hashtable.containsKey() succeed?
/gordon
--
Lew - 17 Jul 2007 14:03 GMT >> Irrelevant. He's comparing HashSets, not their contents. > > The hashcode of the HashSet is based on the hashcodes of the elements > it contains. A second HashSet containing supposedly identical objects, > but whose hashcodes are different, will itself have a different > HashCode. How can Hashtable.containsKey() succeed? You're absolutely right.
 Signature Lew
Gordon Beaton - 17 Jul 2007 14:05 GMT >> Have you confirmed that the HashSet in the HashTable and the object >> you use to find it with, in fact have the same hashCode()? > > Assuming java.util.HashSet, the hashCode() calculation is completely reliable. What makes you think I implied that it wasn't?
/gordon
--
Lew - 17 Jul 2007 14:08 GMT >>> Have you confirmed that the HashSet in the HashTable and the object >>> you use to find it with, in fact have the same hashCode()? >> Assuming java.util.HashSet, the hashCode() calculation is completely reliable. > > What makes you think I implied that it wasn't? Bad thinking on my part. Sorry.
 Signature Lew
George - 17 Jul 2007 14:16 GMT I am refering to the classes available from java.util, you are right. But if you construct a hashset containing objects o1 and o2 (assume any type of objects) and a second hashset containing their clones, would the hashcodes of the objects (and hence the hashcode of the hashset containing them) be the same?
It would seem logical to me that the answer to this question should be positive, but the results of my code suggest otherwise...:(
By overiding equals and hashCode, I get something more closer to the result I want but a bit further from what I would assume as correct behaviour. If you could provide any explanation as to why the answer to my assumption above shoulbe no, I would be grateful.
Thanks, George
On Tue, 17 Jul 2007 Lekeas GK wrote...
|From: Lew <lew@lewscanon.nospam> |Date: Tue, 17 Jul 2007 09:08:32 -0400 [quoted text clipped - 10 lines] |-- |Lew Chris Dollin - 17 Jul 2007 14:26 GMT > I am refering to the classes available from java.util, you are right. But > if you construct a hashset containing objects o1 and o2 (assume any type > of objects) and a second hashset containing their clones, would the > hashcodes of the objects (and hence the hashcode of the hashset containing > them) be the same? No. (Well, it /could/ be. By coincidence. But likely not.)
> It would seem logical to me that the answer to this question should be > positive, but the results of my code suggest otherwise...:( The default hashcode is a magic number (something like the original address of the object, or maybe an allocation count, or something). It's nothing to do with any members you may have in the object. Also the default .equals is ==.
 Signature Hewlett-Packard Limited registered no: registered office: Cain Road, Bracknell, Berks RG12 1HN 690597 England
Lew - 17 Jul 2007 14:34 GMT >> I am refering to the classes available from java.util, you are right. But >> if you construct a hashset containing objects o1 and o2 (assume any type [quoted text clipped - 3 lines] > > No. (Well, it /could/ be. By coincidence. But likely not.) Even if the hash codes of the two HashSets (not "hashset") were the same, the two would still not be equals() unless the objects were equals().
Similarly for any two objects. Even if the hash codes are the same, they don't necessarily equals() each other.
 Signature Lew
Lew - 17 Jul 2007 14:26 GMT > I am refering to the classes available from java.util, you are right. But > if you construct a hashset [sic] containing objects o1 and o2 (assume any type > of objects) and a second hashset [sic] containing their clones, would the > hashcodes [sic] of the objects (and hence the hashcode [sic] of the hashset [sic] containing > them) be the same? Only if the class of the objects in the set overrode equals() to indicate value equality instead of the default reference equality.
So, for example, given a class that does not override equals():
public class Foo { private String f; private String g; // public accessor and mutator methods ... }
two distinct Foo objects f1 and f2 aren't equal even if f1.f.equals( f2.f ) and f1.g.equals( f2.g ). f1 != f2 even if f1.f == f2.f and f1.g == f2.g.
Btw, it's "HashSet".
 Signature Lew
Lew - 17 Jul 2007 14:29 GMT > f1 != f2 even if f1.f == f2.f and f1.g == f2.g. I meant, "! f1.equals( f2 ) even if ...".
Hendrik Maryns - 17 Jul 2007 15:26 GMT George schreef:
> I am refering to the classes available from java.util, you are right. But > if you construct a hashset containing objects o1 and o2 (assume any type > of objects) and a second hashset containing their clones, would the > hashcodes of the objects (and hence the hashcode of the hashset containing > them) be the same? Ah, here’s the catch: clones are not == equal. I.e. obj.clone() != obj. But if you override equals (which you should), then obj.clone().equals(obj) should be true. (Make it true!)
> By overiding equals and hashCode, I get something more closer to the > result I want but a bit further from what I would assume as correct > behaviour. If you could provide any explanation as to why the answer to my > assumption above shoulbe no, I would be grateful. Then you are doing something wrong. Read the link in my sig if you want more help.
And did you consider Lew’s advice to use HashMap instead of Hashtable?
H. - -- Hendrik Maryns http://tcl.sfs.uni-tuebingen.de/~hendrik/ ================== http://aouw.org Ask smart questions, get good answers: http://www.catb.org/~esr/faqs/smart-questions.html
Patricia Shanahan - 17 Jul 2007 21:18 GMT > I am refering to the classes available from java.util, you are right. But > if you construct a hashset containing objects o1 and o2 (assume any type [quoted text clipped - 4 lines] > It would seem logical to me that the answer to this question should be > positive, but the results of my code suggest otherwise...:( These paragraphs strongly imply that you consider clones to be equal. That means two distinct instances of your class can be equal, so the Object equals and hashCode behavior is completely wrong for that class. You do need to override.
> By overiding equals and hashCode, I get something more closer to the > result I want but a bit further from what I would assume as correct > behaviour. If you could provide any explanation as to why the answer to my > assumption above shoulbe no, I would be grateful. "something more closer" suggests that you are not there yet. If so, I suggest doing the following:
1. Write a trivial hashCode() that always returns zero.
2. Concentrate on equals. Think carefully about what determines whether an instance of your class is equal to another object, and implement that test as your equals method.
The hash-based collections work correctly, but inefficiently, with a trivial hashCode().
Once you are sure equals is correct, go back and implement hashCode so that all equal instances have the same hashCode while minimizing collisions between unequal objects.
Patricia
George - 18 Jul 2007 14:56 GMT That is not really the case in my program - I will briefly try to explain the situation.
I have a custom data structure, say custom1, made up of two string fields and another custom data type, say custom2. So, there is a series of custom2 objects creation, then used for a series of custom1 object creation. The custom1 class also has a static HashTable field, indexed by a HashSet. The HashSet is constructed in a separate method which has statements of the type hashsetname.add(new custom3(...)).
When this method finishes, before insertion into the main HashTable I run a check with containsKey - the problem is that it fails even for custom3 objects with exactly the same fields.
Please offer some advice if you can, as I am stuck at this point for about a week now and it brought all my work to a halt.
Regards, George
On Tue, 17 Jul 2007 Lekeas GK wrote...
|From: Patricia Shanahan <pats@acm.org> |Date: Tue, 17 Jul 2007 13:18:19 -0700 [quoted text clipped - 36 lines] | |Patricia Lew - 18 Jul 2007 15:13 GMT > That is not really the case in my program - I will briefly try to explain > the situation. Please do not top-post - it makes it harder to understand to what you are responding.
/What/ is really not the case? That you need to override equals()? But you most certainly do need to.
> I have a custom data structure, say custom1, made up of two string fields > and another custom data type, say custom2. So, there is a series of > custom2 objects creation, then used for a series of custom1 object > creation. The custom1 class also has a static HashTable field, indexed by Again, it's "Hashtable", not "HashTable".
> a HashSet. The HashSet is constructed in a separate method which has > statements of the type hashsetname.add(new custom3(...)). Thus guaranteeing that the inserted objects do not == objects in another Set.
> When this method finishes, before insertion into the main HashTable I run > a check with containsKey - the problem is that it fails even for custom3 > objects with exactly the same fields. Because you did not override equals(), as has been repeatedly explained.
> Please offer some advice if you can, as I am stuck at this point for about > a week now and it brought all my work to a halt. My advice is to follow the advice you've already been given.
From: Patricia Shanahan <pats@acm.org>
> |These paragraphs strongly imply that you consider clones to be equal. > |That means two distinct instances of your class can be equal, so the > |Object equals and hashCode behavior is completely wrong for that class. > |You do need to override. ...
> |"something more closer" suggests that you are not there yet. If so, I > |suggest doing the following: [quoted text clipped - 11 lines] > |that all equal instances have the same hashCode while minimizing > |collisions between unequal objects.
 Signature Lew
Patricia Shanahan - 18 Jul 2007 15:33 GMT > That is not really the case in my program - I will briefly try to explain > the situation. I'm afraid I don't really understand this remark, because you also say "even for custom3 objects with exactly the same fields" which also strongly suggest that two distinct custom3 instances can be equal, exactly the situation I was talking about.
> I have a custom data structure, say custom1, made up of two string fields > and another custom data type, say custom2. So, there is a series of [quoted text clipped - 9 lines] > Please offer some advice if you can, as I am stuck at this point for about > a week now and it brought all my work to a halt. ...
I'm afraid the only advice that will work is essentially the advice everyone has been giving you.
You need to look VERY closely at the equals implementation for custom3.
First temporarily take hashCode out of the picture by implementing it in each customN class as follows:
public int hashCode(){ return 0; }
That will make HashSet inefficient, but make it impossible for hashCode bugs to prevent correct HashSet operation. When everything is working functionally you should implement a good hashCode for each class, matching its equals method.
You need to work through each of your classes, implementing AND TESTING equals. Make sure you test equals with parameter type Object, not custom3 etc.
I would begin with custom2, because it seems to be bottom of the dependency hierarchy. Can two custom2 objects be equal because they have equal fields, without being the same object? If so, write an equals method that tests whether they have the same fields.
At each step, make sure you have a overridden hashCode with the trivial version, and that you have tested equals and are happy with the results.
Once custom3's equals method returns true exactly when you think the two objects are equal, your HashSet containsKey test will work just fine.
If this advice does not solve your problem, I suggest an SSCCE: http://www.physci.org/codes/sscce/
Patricia
Patricia Shanahan - 18 Jul 2007 17:01 GMT > I have a custom data structure, say custom1, made up of two string fields > and another custom data type, say custom2. So, there is a series of > custom2 objects creation, then used for a series of custom1 object > creation. The custom1 class also has a static HashTable field, indexed by > a HashSet. The HashSet is constructed in a separate method which has > statements of the type hashsetname.add(new custom3(...)). Here's a different approach to explaining what I think is going on. I've written two trivial classes, NoEquals and HasEquals. Each has two String fields. The difference is that NoEquals inherits equals and hashCode from Object. HasEquals has a trivial, dummy hashCode and an equals that treats HasEquals instances with equal fields as being equal.
I also wrote a test method that takes a pair of references and does some tests of the sorts of things that I gather, from your messages, are going on in your program, constructing a Set of instances of the class and using it as a Map key. It is called three times, with a pair of NoEquals objects, with an equal pair of HasEquals objects, and with an unequal pair of HasEquals objects.
The test demonstrates how the results of the Set contains and Map containsKey methods depend on the underlying equals implementation in my classes. If this program does not explain what is going on in your code, try to modify it to demonstrate the problem and post the result.
Output:
NoEquals objects left == right is false left.equals(right) is false s1.contains(right) is false m.containsKey(s1) is false
equal HasEquals objects left == right is false left.equals(right) is true s1.contains(right) is true m.containsKey(s1) is true
unequal HasEquals objects left == right is false left.equals(right) is false s1.contains(right) is false m.containsKey(s1) is false
Source code - copy to an EqualsDemo.java file:
import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set;
public class EqualsDemo {
static void test(Object left, Object right) { System.out.printf("left == right is %b%n", left == right); System.out.printf("left.equals(right) is %b%n", left .equals(right)); Set<Object> s1 = new HashSet<Object>(); s1.add(left); System.out.printf("s1.contains(right) is %b%n", s1 .contains(right)); Map<Object, String> m = new HashMap<Object, String>(); m.put(s1, "Dummy string"); Set<Object> s2 = new HashSet<Object>(); s2.add(right); System.out.printf("m.containsKey(s1) is %b%n", m .containsKey(s2)); }
public static void main(String[] args) { System.out.println("NoEquals objects"); test(new NoEquals("a", "b"), new NoEquals("a", "b")); System.out.println(); System.out.println("equal HasEquals objects"); test(new HasEquals("a", "b"), new HasEquals("a", "b")); System.out.println(); System.out.println("unequal HasEquals objects"); test(new HasEquals("a", "b"), new HasEquals("a", "B")); } }
class NoEquals { String field1; String field2; NoEquals(String field1, String field2) { this.field1 = field1; this.field2 = field2; } }
class HasEquals { String field1; String field2; HasEquals(String field1, String field2) { this.field1 = field1; this.field2 = field2; } public boolean equals(Object obj) { if (obj == null || !(obj instanceof HasEquals)) { return false; } else { HasEquals other = (HasEquals) obj; return field1.equals(other.field1) && field2.equals(other.field2); } }
/* * Dummy hashCode for testing equals, do not use in production * code. */ public int hashCode() { return 0; } }
Piotr Kobzda - 19 Jul 2007 08:59 GMT [...]
Just a small comment to your nice demo:
> if (obj == null || !(obj instanceof HasEquals)) { Since null is never an instance of any class, enough is to say:
if (!(obj instanceof HasEquals)) {
piotr
Twisted - 19 Jul 2007 19:57 GMT > [...] > [quoted text clipped - 5 lines] > > if (!(obj instanceof HasEquals)) { Does this definitely work? Any language lawyers around? I can see three sensible possibilities:
null instanceof HasEquals == true (since HasEquals foo = null; is legal) null instanceof HasEquals == false (since no constructed HasEquals instance is null) null instanceof HasEquals throws NullPointerException (since the reference is null)
and I don't see that it's in any way "obvious" which of the three the Java folks would have chosen. All make their own kind of sense.
Patricia Shanahan - 19 Jul 2007 20:13 GMT >>[...] >> [quoted text clipped - 18 lines] > and I don't see that it's in any way "obvious" which of the three the > Java folks would have chosen. All make their own kind of sense. Fortunately, the JLS resolves the issue: "At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast (§15.16) to the ReferenceType without raising a ClassCastException. Otherwise the result is false."
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.20.2
Piotr is indeed right, and the expression could be simplified. However, I will probably still go on coding the explicit null check in cases in which it is not necessary, because I think it makes the correctness of the code more obvious.
Patricia
Lasse Reichstein Nielsen - 19 Jul 2007 20:40 GMT >> if (!(obj instanceof HasEquals)) { > > Does this definitely work? Yes
> Any language lawyers around? I can see three sensible possibilities: > > null instanceof HasEquals == true (since HasEquals foo = null; is > legal) Nope. JLS 3 section 15.20.2: "At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast (§15.16) to the ReferenceType without raising a ClassCastException. Otherwise the result is false." <URL:http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.20.2>
> null instanceof HasEquals == false (since no constructed HasEquals > instance is null) It's false, just because that's what the specification of "instanceof" says. But this would be a good argument for that decission. It's quite simple to say whether the null reference points to an instance of a specific class. It doesn't.
> and I don't see that it's in any way "obvious" which of the three the > Java folks would have chosen. All make their own kind of sense. Indeed. I was guessing the first one myself at one point (assignment is legal, so instanceof must agree), which caused me to learn the real answer the hard way - it's the best way to learn if you want to remember the answer :)
/L
 Signature Lasse Reichstein Nielsen - lrn@hotpop.com DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html> 'Faith without judgement merely degrades the spirit divine.'
Twisted - 21 Jul 2007 11:04 GMT > Indeed. I was guessing the first one myself at one point (assignment > is legal, so instanceof must agree), which caused me to learn the > real answer the hard way - it's the best way to learn if you want > to remember the answer :) IMO, Patricia is right and it's better to not rely on something unobvious like that with multiple plausible interpretations. I will continue to code explicit null checks as well. The compiler should be able to optimize them out if you two are correct about the specification, so it shouldn't be a performance hit or anything like that.
rossum - 17 Jul 2007 16:00 GMT >Dear All, > [quoted text clipped - 9 lines] > >George Have a look at Item 8: "Always override hashCode when you override equals" in Chapter 3 of "Effective Java": http://developer.java.sun.com/developer/Books/effectivejava/Chapter3.pdf
rossum
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|