dacanalr
dacanalr

Reputation: 193

Map inside Map in Scala

I've this code :

val total = ListMap[String,HashMap[Int,_]]
val hm1 = new HashMap[Int,String]
val hm2 = new HashMap[Int,Int]
...
//insert values in hm1 and in hm2
...
total += "key1" -> hm1
total += "key2" -> hm2

....

val get = HashMap[Int,String] = total.get("key1") match {
  case a : HashMap[Int,String] => a
}

This work, but I would know if exists a better (more readable) way to do this. Thanks to all !

Upvotes: 3

Views: 4887

Answers (4)

Paolo Falabella
Paolo Falabella

Reputation: 25834

First of all: this is a non-answer (as I would not recommend the approach I discuss), but it was too long for a comment.

If you haven't got too many different keys in your ListMap, I would suggest trying Malvolio's answer. Otherwise, due to type erasure, the other approaches based on pattern matching are practically equivalent to this (which works, but is very unsafe):

val get = total("key1").asInstanceOf[HashMap[Int, String]]

the reasons why this is unsafe (unless you like living dangerously) are:

  1. total("key1") is not returning an Option (unlike total.get("key1")). If "key1" does not exist, it will throw a NoSuchElementException. I wasn't sure how you were planning to manage the "None" case anyway.
  2. asInstanceOf will also happily cast total("key2") - which should be a HashMap[Int, Int], but is at this point a HashMap[Int, Any] - to a HashMap[Int, String]. You will have problem later on when you try to access the Int value (which now scala believes is a String)

Upvotes: 0

Didier Dupont
Didier Dupont

Reputation: 29528

Your total map, containing maps with non uniform value types, would be best avoided. The question is, when you retrieve the map at "key1", and then cast it to a map of strings, why did you choose String?

The most trivial reason might be that key1 and so on are simply constants, that you know all of them when you write your code. In that case, you probably should have a val for each of your maps, and dispense with map of maps entirely.

It might be that the calls made by the client code have this knowledge. Say that the client does stringMap("key1"), or intMap("key2") or that one way or another, the call implies that some given type is expected. That the client is responsible for not mixing types and names. Again in that case, there is no reason for total. You would have a map of string maps, a map of int maps (provided that you are previous knowledge of a limited number of value types)

What is your reason to have total?

Upvotes: 0

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297155

(edit: oh, sorry, I get it now)

Here's the thing: the code above doesn't work. Type parameters are erased, so the match above will ALWAYS return true -- try it with key2, for example.

If you want to store multiple types on a Map and retrieve them latter, you'll need to use Manifest and specialized get and put methods. But this has already been answers on Stack Overflow, so I won't repeat myself here.

Upvotes: 0

Michael Lorton
Michael Lorton

Reputation: 44376

It looks like you're trying to re-implement tuples as maps.

val total : ( Map[Int,String], Map[Int,Int]) = ...

def get : Map[Int,String] = total._1

Upvotes: 4

Related Questions