1

I'm trying to understand the following code, where acct1 and acct2 are objects that represent "bank accounts".

HashMap map = new HashMap();
map.put(acct1.hashCode(), acct1);
map.put(acct2.hashCode(), acct2);

for (Object o : map.values())
{
    System.out.println(o);
}

for (Object o : map.keySet())
{
    System.out.println(o);
}

According to the BankAccount API, the method hashCode() returns an int, which is supposed to be a hash of the account.

So doesn't that mean that the set created by keySet() contains integers? And if so, why is the second for-each iterator declaring its members as Object. When I tried to switch Object to int, I got a compiler error.

yroc
  • 886
  • 1
  • 10
  • 16
  • It's generally not good practice to declare and instantiate genericized classes the way you do. Instead, supply an actual type argument. See [this](https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html) – Michael Jun 02 '16 at 21:00
  • 3
    This code is either ancient or written by someone whose knowledge hasn't been up to date since 2004, when Java 5 introduced generics. – user2357112 Jun 02 '16 at 21:01
  • 1
    It can't be ancient if it uses for-each, though. – RaminS Jun 02 '16 at 21:02
  • @Michael The code is from a course I'm currently taking at a university (I suppose I won't say which!). I didn't write it -- just trying to understand it. – yroc Jun 02 '16 at 21:04
  • Take a look especially at this answer http://stackoverflow.com/a/608/1743880 also from the linked question. – Tunaki Jun 02 '16 at 21:07
  • @Tunaki: I don't think a discussion of `int` vs `Integer` really answers this. Declaring `o` as an `Integer` wouldn't have worked either. This is more a problem of the static type of the map and how the static type of its keys is `Object`, even if all the keys are `Integer`s. – user2357112 Jun 02 '16 at 21:11
  • @user2357112 Can you elaborate? (perhaps as an answer)? – yroc Jun 02 '16 at 21:17
  • @user2357112 Yes it wouldn't have worked either. Reading again, the question is more focused on the `Object` problem coming from the raw type. Reopened, the dupe is indeed incorrect. – Tunaki Jun 02 '16 at 21:21
  • The short answer is because Java `Collection`(s) cannot hold primitive types. – Elliott Frisch Jun 02 '16 at 21:22
  • The map.put(...) is doing implicitly an autoboxing to an Integer, and an integer ist also an Object, so the second for is OK. Try to cast explicitly to Integer instead of int, that is a primitive type, and it should work... – Paolof76 Jun 02 '16 at 21:27
  • @ElliottFrisch So are you saying that when `acct1.hashCode()` is put into the map, it's converted to an `Object`? – yroc Jun 02 '16 at 21:32
  • @Paolof76 If that's the case then why doesn't it work when you declare `o` as an `Integer` (as pointed out by @user2357112) – yroc Jun 02 '16 at 21:33
  • Hi @yroc, I wrote a post instead... it's clearer than comments... I tested and it works. What do you mean it doesn't work? Please refer to the code I wrote... – Paolof76 Jun 02 '16 at 21:36
  • @Paolof76 Thanks for your answer. I meant if you do `for(Integer o : map.keySet())` it still gives a type mismatch compiler error. – yroc Jun 02 '16 at 21:40
  • Just read my posted link, then you'll understand. – Tom Jun 02 '16 at 21:41
  • 1
    @yroc OKI understand your question. The response is, because the non generic Map.keySet returns a set of Objects, and so you can't use Integer in the for-each constructor if you don't give the hint to the compiler that this is a Map – Paolof76 Jun 02 '16 at 21:41
  • @yroc updated answer ;) – Paolof76 Jun 02 '16 at 21:45
  • @Paolof76 Thanks. It's not that I "want" to use `Integer` in the `for-each`, it's just that I'm trying to understand why the map's members are being typed as `Object` in the `for-each` when they are ostensibly `int`s (according to the `BankAccount` API). – yroc Jun 02 '16 at 21:49
  • 2
    @yroc because the keySet is implementing the Map interface: https://docs.oracle.com/javase/7/docs/api/java/util/Map.html If you use the raw type, this implementation is returning a Set (that is a raw Set). If you give the hint to the compiler, it works accordingly returning the Set you want – Paolof76 Jun 02 '16 at 21:58
  • On a related topic, the hashCode() method was not designed to be used that way. A HashMap and HashSet themselves call hashCode() on the objects they store, so you don't have to call it explicitly. Instead of adding accounts to a HashMap keyed by their hashCodes, consider adding the accounts directly into a HashSet. – Klitos Kyriacou Jun 02 '16 at 23:08

1 Answers1

1

The map.put(...) is doing implicitly an autoboxing to an Integer, and an Integer is also an Object, so the second for is fine. If you cast explicitly to Integer instead of int your Object (int is a primitive type and would give compilation error) it works:

@Test
public void mytest(){
    Object acct1 = new Object();
    Object acct2 = new Object();
    HashMap map = new HashMap();
    map.put(acct1.hashCode(), acct1);
    map.put(acct2.hashCode(), acct2);

    for (Object o : map.values())
    {
        System.out.println(o);
    }

    for (Object o : map.keySet())
    {
        System.out.println(o);
        System.out.println((Integer)o);
    }
}

EDIT: Note that the use of Object in for-each is needed because the compiler doesn't know which particular object is stored as key, so it is using the Object, that is "the root of the class hierarchy. Every class has Object as a superclass" (javadoc).

If you want to use Integer in the for-each, you need to give the hint to the compiler that this is a Map <Integer,Object>, simply using the generics syntax. This code will compile:

@Test
public void mytest(){
    Object acct1 = new Object();
    Object acct2 = new Object();
    HashMap<Integer, Object> map = new HashMap<>();
    map.put(acct1.hashCode(), acct1);
    map.put(acct2.hashCode(), acct2);

    for (Object o : map.values())
    {
        System.out.println(o);
    }

    for (Integer o : map.keySet())
    {
        System.out.println(o);
    }
}
Paolof76
  • 889
  • 1
  • 9
  • 23
  • 1
    The cast to `Integer` in the last `println()` statement is redundant. +1 for everything else. :-) – PNS Jun 02 '16 at 21:51