Reputation: 43330
When I create an immutable map with a standard call to Map()
or by concatenating the existing maps created that way, in all my tests I get that traversing its members provides them in the order of addition. That's exactly the way I need them to be sorted, but there's not a word in the documentation about the reliability of the ordering of the members of the map.
So I was wondering whether it is safe to expect the standard Map to return its items in the order of addition or I should look for some other implementations and which ones in that case.
Upvotes: 2
Views: 247
Reputation: 43330
After digging I've found out that there exists an immutable ListMap
that behaves exactly as I want it, but according to this table its performance is just awfull. So I wrote a custom immutable implementation that should perform effectively on all operations except removal, where it performs linearly. It does require a bit more memory as it's backed by a standard Map
and a Queue
, which itself utilizes a List
twice, but in the current age it's not an issue, right.
import collection.immutable.Queue
object OrderedMap {
def apply[A, B](elems: (A, B)*) =
new OrderedMap(Map(elems: _*), Queue(elems: _*))
}
class OrderedMap[A, B](
map: Map[A, B] = Map[A, B](),
protected val queue: Queue[(A, B)] = Queue()
) extends Map[A, B] {
def get(key: A) =
map.get(key)
def iterator =
queue.iterator
def +[B1 >: B](kv: (A, B1)) =
new OrderedMap(
map + kv,
queue enqueue kv
)
def -(key: A) =
new OrderedMap(
map - key,
queue filter (_._1 != key)
)
override def hashCode() =
queue.hashCode
override def equals(that: Any) =
that match {
case that: OrderedMap[A, B] =>
queue.equals(that.queue)
case _ =>
super.equals(that)
}
}
Upvotes: 1
Reputation: 3608
There is no promise about the order of Map
. There is an OrderedMap
in scalas collection package. The values in that package are ordered by an implicit Ordering
. As quickfix I recommend you to use a list of keys for the ordering of your Map.
var keyOrdering = List[Int]()
var unorderedMap = Map[Int, String]()
unorderedMap += (1 -> "one")
keyOrdering :+= 1
Edit
You could implement your own Ordering
and pass it to a SortedMap
as well.
Edit #2
A simple example would be the following:
scala> import scala.collection.SortedMap
import scala.collection.SortedMap
scala> implicit object IntOrdering extends Ordering[Int]
| def compare(a: Int, b: Int) = b - a
| }
defined module IntOrdering
scala> var sm = SortedMap[Int, String]()
sm: scala.collection.SortedMap[Int,String] = Map()
scala> sm += (1 -> "one")
scala> sm += (2 -> "two")
scala> println(sm)
Map(2 -> two, 1 -> one)
The implicit Ordering is applied to the keys, so IntOrdering
might be applied to a SortedMap[Int, Any]
.
Edit #3
A self ordering DataType like in my comment might look this way:
case class DataType[T](t: T, index: Int)
object DataType{
private var index = -1
def apply[T](t: T) = { index += 1 ; new DataType[T](t, index)
}
Now we need to change the Ordering:
implicit object DataTypeOrdering extends Ordering[DataType[_]] {
def compare(a: DataType[_], b: DataType[_]) = a.index - b.index
}
I hope this is the way you expected my answer.
Upvotes: 1
Reputation: 340913
I don't think it's safe, the order is not preserved starting from 5 elements (Scala 2.9.1):
scala> Map(1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4, 5 -> 5)
res9: scala.collection.immutable.Map[Int,Int] =
Map(5 -> 5, 1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4)
With bigger maps the order is completely "random", try Map((1 to 100) zip (1 to 100): _*)
.
Try LinkedHashMap
for ordered entries and TreeMap
to achieve sorted entries.
Upvotes: 5