Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > HashMap get/put

Reply
Thread Tools

HashMap get/put

 
 
Mike Schilling
Guest
Posts: n/a
 
      10-29-2009
Wojtek wrote:
> If I have the following:
>
> HashMap<MyKey,MyValue> map = new HashMap<MyKey,MyValue>();
> MyKey myKey = new MyKey();
>
> Then I can put in a value by:
>
> map.put(myKey, new MyValue() );
>
> The compiler enforces the use of these two types.
>
> However to do a get I can do the following and the compiler does not
> complain:
>
> map.get(myKey); // this is right
> map.get(myKey.toString()); // this is wrong yet legal
> map.get(new Long(20)); // this is wrong yet legal
>
> All of these are legal according to the compiler. Why is it that the
> compiler does not enforce type checking on the get()?
>
> Or rather, why does the spec not say get(K key) instead of
> get(Object
> o)
>
> http://java.sun.com/javase/6/docs/ap...va.lang.Object)


I can picture (vaguely) looking something up with an object that's
equal to the original key but not of the same type. This makes at
least some sense with HashMaps, since it's possible, for objects T1 a
and T2 b, where T1 and T2 are any types at all, that a.equals(b) and
a.hashCode() == b.hashCode().

TreeMap is weirder:

Map<Integer, String> m = new TreeMap<Integer,String>();
m.put(2, "foo");
String val = m.get("bar");

This compiles just fine, but results in

Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer
at java.lang.String.compareTo(String.java:90)
at java.util.TreeMap.compare(TreeMap.java:1093)
at java.util.TreeMap.getEntry(TreeMap.java:347)
at java.util.TreeMap.get(TreeMap.java:265)
at WeirdMap.main(WeirdMap.java:11)

Since a String can't even be compared to the Integer keys.

Actually, it strikes me as odd that String.compare() is called rather
than Integer.compare(). I suppose that if you want precise control
over how comparisons are done, you need to pass in your own
Comparator.


 
Reply With Quote
 
 
 
 
Lew
Guest
Posts: n/a
 
      10-29-2009
Mike Schilling wrote:
> I can picture (vaguely) looking something up with an object that's
> equal to the original key but not of the same type. This makes at
> least some sense with HashMaps, since it's possible, for objects T1 a
> and T2 b, where T1 and T2 are any types at all, that a.equals(b) and
> a.hashCode() == b.hashCode().


I don't think so, not in general nor commonly. Someone who codes disparate
types that way should be slapped, then fired.

Remember, to work properly 'equals()' must be reflexive, symmetric and
transitive. However, you are right that it's possible to violate that.
Possible, but stupid.

The common idiom for 'equals()' checks that the argument is of the same type
as the invoker.

--
Lew
 
Reply With Quote
 
 
 
 
Wojtek
Guest
Posts: n/a
 
      10-29-2009
Lew wrote :
> And this is relevant how? 'Map' retrieval is defined in terms of 'equals()',
> not 'hashCode()'.


The initial lookup up is done with a hashcode.

You could contrive a case where the hashcode is randomly generated for
a given object. It will never be found in the HashMap, yet the equals
would return true.

--
Wojtek


 
Reply With Quote
 
Wojtek
Guest
Posts: n/a
 
      10-29-2009
Lew wrote :
> FWIW, if 'Map#get()' had been redefined generically, it would have broken
> existing code without providing an improvement. Code that would have not
> gotten a value still does not get a value without introducing a new error.
> Maybe they erred in this, but they were trying to maintain backward
> compatibility.


All sorts of code broke when generics were introduced. Every use of
each class which had generics needed to be changed. Also changing get()
would just have been done at that time.

> It's a shame that the compiler didn't find your refactoring mistake for you,
> but it was your mistake, not Java's.


Of course it was my mistake. That is what I am saying. And that is why
we have compilers which report mistakes, so that they may be caught
BEFORE they show up during runtime and "weird things" happen.

--
Wojtek


 
Reply With Quote
 
Wojtek
Guest
Posts: n/a
 
      10-29-2009
Stefan Ram wrote :
> http://www.velocityreviews.com/forums/(E-Mail Removed)-berlin.de (Stefan Ram) writes:
>> Look at the following example: (...)
>> final java.util.Map<java.lang.String,java.lang.String> value0
>> = new java.util.HashMap<java.lang.String,java.lang.Strin g>(); (...)
>> »value1« has not the type used in the map, yet the »get« succeeds.

>
> Above, both types were the same after type erasure and were
> types of empty containers.
>
> Here is a program with types that differ even after type
> erasure and non-empty containers.
>

Um, I did a bit of editing in your example, removing the packages. For
some reason this makes it easier for me to read...

public class Main
{ public static void main( final String[] args )
{
final ArrayList<String> list0 = new ArrayList<String >();
list0.add( "text" );

final LinkedList<String > list1 = new LinkedList<String >();
list1.add( "text" );

final Map<ArrayList<String>,String> map = new
HashMap<ArrayList<String>,String >();

map.put( list0, "value" );

System.out.println( map.get( list1 ));
}
}

I do not see why this works or why this should work. Maybe because
"text" was added to both list0 and list1?

--
Wojtek


 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      10-29-2009
Wojtek wrote:
> Um, I did a bit of editing in your example, removing the packages. For
> some reason this makes it easier for me to read...


Amen to that!

Who puts FQNs into java.lang classes?

Stefan, your examples would be much, much easier to read if you'd
follow the coding conventions and also use simple type names where the
context is clear and for java.lang classes. Your lack of
conventionality turned that example into garbage.

--
Lew


 
Reply With Quote
 
Dave Searles
Guest
Posts: n/a
 
      10-29-2009
Patricia Shanahan wrote:
> Wojtek wrote:
>> Lew wrote :
>>> And this is relevant how? 'Map' retrieval is defined in terms of
>>> 'equals()', not 'hashCode()'.

>>
>> The initial lookup up is done with a hashcode.
>>
>> You could contrive a case where the hashcode is randomly generated for
>> a given object. It will never be found in the HashMap, yet the equals
>> would return true.

>
> You could not have a random hashcode that conforms to the Object
> hashCode contract, because of the requirement that two objects that are
> equal according to their equals methods have the same hashCode result.


Actually, you could if the equals method always returned false. Then
hashCode conforms. But equals may not, if the equals contract requires
that if a == b then a.equals(b). In that case, a random-but-set-at-birth
hashCode and equals equivalent to == is as close as you can get while
conforming to BOTH cotnracts (and java.lang.Object basically is set up
like that).
 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      10-29-2009
Mike Schilling wrote:
>>> I can picture (vaguely) looking something up with an object that's
>>> equal to the original key but not of the same type. *This makes at
>>> least some sense with HashMaps, since it's possible, for objects T1 a
>>> and *T2 b, where T1 and T2 are any types at all, that a.equals(b) and
>>> a.hashCode() == b.hashCode().

>


Lew wrote:
>> I don't think so, not in general nor commonly. *Someone who codes
>> disparate types that way should be slapped, then fired.

>


Patricia Shanahan wrote:
> That is correct if "disparate" is read as meaning distinct types that do
> are not sufficiently related that objects of the two types might be equal..
>


Yes, that's what I meant. Your example of different implementations
for 'List' would not be "disparate" in this sense.

Lew:
>> Remember, to work properly 'equals()' must be reflexive, symmetric and
>> transitive. *However, you are right that it's possible to violate that..
>> Possible, but stupid.

>


Patricia:
> It is possible to have equals methods that allow equality to an object
> of a different type, not a subclass or superclass, that is still
> reflexive, symmetric, and transitive.
>
> Consider, for example, ArrayList and Stack. They both conform to the
> java.util.List interface, including its documentation for equals:
> "Returns true if and only if the specified object is also a list, both
> lists have the same size, and all corresponding pairs of elements in the
> two lists are equal."
>


These types are not completely disparate, but even to the degree to
which they are they adhere to the key requirements as you point out.
They also provide a strong argument for 'Map#get(Object)' instead of
'Map#get(E)'.

> The really, really, basic rule about equals is 'Indicates whether some
> other object is "equal to" this one.'. One would need a breach of that
> rule to get a false match from HashMap's get.
>
> The decision to leave get accepting Object means that one can still
> probe a Map<Stack, String> with an ArrayList, and get a match if, only
> if, the Map contains a key that has equal elements in the same order as
> the ArrayList.
>


Lew:
>> The common idiom for 'equals()' checks that the argument is of the
>> same type as the invoker.

>


Patricia:
> The more general version of this rule is to test for types of objects
> that might be logically equal to this one. A List equals() method should
> test whether its argument is a List, not whether it is the same type as
> the specific List implementation.


Excellent points that explain why Java might not have wanted to
genericize 'Map#get()'. They might've done better with some sort of
bounded wildcard argument, but 'Object' works just fine.

The key insight from your example is that for the purpose of
comparison, 'List' implementations are viewed at the level of the
interface type, and not at the level of the concrete type. At that
level, 'ArrayList' and 'Stack' are not disparate.

--
Lew
 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      10-30-2009
Wojtek wrote:
> Lew wrote :
>> FWIW, if 'Map#get()' had been redefined generically, it would have
>> broken existing code without providing an improvement. Code that
>> would have not gotten a value still does not get a value without
>> introducing a new error. Maybe they erred in this, but they were
>> trying to maintain backward compatibility.

>
> All sorts of code broke when generics were introduced. Every use of each
> class which had generics needed to be changed. Also changing get() would
> just have been done at that time.
>
>> It's a shame that the compiler didn't find your refactoring mistake
>> for you, but it was your mistake, not Java's.

>
> Of course it was my mistake. That is what I am saying. And that is why
> we have compilers which report mistakes, so that they may be caught
> BEFORE they show up during runtime and "weird things" happen.


But you cannot expect the compiler to catch all your mistakes. So this was
one that it couldn't catch because in their infinite wisdom the Powers That Be
decreed that 'Map#get()' not be type parameterized. A few minutes with the
'Map' Javadocs before you refactored would've saved you some trouble. Then
you could've used Eclipse's Ctrl-Shift-G (find all references) or the
equivalent for your IDE and fixed the problem with even less effort than
letting the compiler find the error would have caused.

I think we're spoiled by how much help Java gives us to catch and prevent
error. We start whining like little brats when we reach one of those corners
that we have to sweep with our own effort.

You did the community a good service by alerting us to this particular corner.

--
Lew
 
Reply With Quote
 
Mike Schilling
Guest
Posts: n/a
 
      10-30-2009
> But you cannot expect the compiler to catch all your mistakes. So
> this was one that it couldn't catch because in their infinite wisdom
> the Powers That Be decreed that 'Map#get()' not be type
> parameterized.


Which we still don't have a good explanation for. The closest to a
real use case for get(Object) rather than get(K) we've seen is
Patricia's, and that could be made to work with get(K) simply enough
by letting K be List instead of ArrayList.


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
reuse HashMap$Entry (or HashMap in total) to avoid millions of allocations Vince Darley Java 4 03-02-2010 07:48 AM
java.util.Properties extending from HashMap<Object, Object> insteadof HashMap<String, String> Rakesh Java 10 04-08-2008 04:22 AM
HashMap vs TreeMap Ahmed Moustafa Java 2 08-10-2003 03:31 AM
Re: Properties2 extends Hashmap, pros and cons? Jon Skeet Java 5 07-08-2003 06:44 PM
HashMap Sanjay Kumar Java 2 07-05-2003 07:10 PM



Advertisments