2012-05-21 19 views
2

當我創建一個不可變的地圖時,通過對Map()的標準調用或通過連接現有地圖創建這種方式,在所有我的測試中,我得到遍歷它的成員按添加的順序提供它們。這正是我需要對它們進行排序的方式,但文檔中沒有關於地圖成員排序可靠性的文字。地圖成員的排序是否似乎是可靠的?

所以我想知道是否可以安全地期望標準Map按照添加的順序返回它的項目,或者我應該尋找其他的實現以及在這種情況下的哪些實現。

+0

通常不建議依靠實施細節。也就是說,'scala.collection.immutable.Map [A​​,B]'實現'Iterable [(A,B)]'而不是'Seq [(A,B)]',這將保證順序。 – ziggystar

+0

而不是使用'Map [K,V]'你可以使用'Map [K,(V,Int)]'其中'Int'它的插入索引。然後調用'toSeq'並在該字段上排序,如果需要將所有對提取爲一個序列。 –

回答

5

我不認爲它是安全的,不保留的順序從5個元素(斯卡拉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) 

有了更大的映射的順序是完全「隨機」,嘗試Map((1 to 100) zip (1 to 100): _*)

嘗試LinkedHashMap訂購條目和TreeMap以實現排序條目。

+0

Yikes!這在2.10.0-M3中是可重現的。好的,對於問題的第二部分:對於我的情況,您會推薦哪種實現? –

+0

@NikitaVolkov:嘗試['LinkedHashMap'](http://www.scala-lang.org/api/current/scala/collection/mutable/LinkedHashMap.html)訂購條目和['TreeMap'](http:/ /www.scala-lang.org/api/current/scala/collection/immutable/TreeMap.html)來實現排序條目。 –

+0

謝謝,但雖然'LinkedHashMap'的確可以正確地排列項目,但它是可變的。我知道不可變的'ListMap',它也可以做到這一點,但根據[this](http://www.scala-lang.org/docu/files/collections-api/collections_40.html),它的表現非常好。所以看起來我必須處理它或進行自定義實現 - 對嗎? –

1

對於Map的訂單沒有承諾。在scalas收集包中有一個OrderedMap。該包中的值由隱含的Ordering排序。作爲quickfix,我建議你使用一個鍵列表來排列你的Map。

var keyOrdering = List[Int]() 
var unorderedMap = Map[Int, String]() 

unorderedMap += (1 -> "one") 
keyOrdering :+= 1 

編輯

你可以實現自己的Ordering,並把它傳遞給一個SortedMap爲好。

編輯#2

一個簡單的例子是下面的:

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) 

隱式排序被應用到密鑰,所以IntOrdering可能被施加到SortedMap[Int, Any]

編輯#3

自訂貨數據類型一樣在我的評論看起來是這樣的:

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

現在,我們需要改變排序:

implicit object DataTypeOrdering extends Ordering[DataType[_]] { 
    def compare(a: DataType[_], b: DataType[_]) = a.index - b.index 
} 

希望這是你期待我的答案的方式。

+1

你可否詳細說一下'Ordering'和'SortedMap'多一點? –

+0

我用一個簡單的例子更新了我的答案。 –

+0

謝謝,但它是不正確的:你通過鍵排序的地圖,而不是通過添加順序在這兩個示例 –

1

挖掘之後,我發現存在一個不可變的ListMap,其行爲與我想要的完全一樣,但根據this table,其性能只是很差。所以我寫了一個自定義的不可變實現,它應該對除了刪除之外的所有操作都有效地執行,並在其中線性執行。它確實需要更多的內存,因爲它有一個標準的MapQueue作爲後盾,後者本身使用了List兩次,但在當前時代這不是問題,對。

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) 
    } 
} 
相關問題