HashTable

P

Patricia Shanahan

George said:
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
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.
....

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
 
P

Patricia Shanahan

George said:
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;
}
}
 
P

Piotr Kobzda

Patricia Shanahan wrote:

[...]

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
 
T

Twisted

Patricia Shanahan wrote:

[...]

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)) {

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.
 
P

Patricia Shanahan

Twisted said:
Patricia Shanahan wrote:

[...]

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)) {


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.

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
 
L

Lasse Reichstein Nielsen

Twisted said:
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."
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
 
T

Twisted

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,216
Latest member
topweb3twitterchannels

Latest Threads

Top