首页 文章

Scala集合标准实践

提问于
浏览
31

来自 Java 背景,我习惯于处理集合的常见做法:显然会有异常但通常代码看起来像:

public class MyClass {
  private Set<String> mySet;

  public void init() {
    Set<String> s = new LinkedHashSet<String>();
    s.add("Hello");
    s.add("World"); 
    mySet = Collections.unmodifiableSet(s);
  }
}

我不得不承认,我对Scala中的众多选项感到有些困惑 . 有:

  • scala.List (和 Seq

  • scala.collections.Set (和 Map

  • scala.collection.immutable.Set (和 MapStack 但不是 List

  • scala.collection.mutable.Set (和 MapBuffer 但不是 List

  • scala.collection.jcl

所以问题!

  • 为什么 ListSeq 在包 scala 中定义而不是 scala.collection (即使 Seq 的实现在集合子包中)?

  • 初始化集合然后冻结它的标准机制是什么(在Java中是通过包装 unmodifiable 来实现的)?

  • 为什么某些集合类型(例如 MultiMap )仅定义为可变? (没有不可变的 MultiMap )?

我仍然对在实践中如何实际使用它们感到困惑 . 由于强制完整的包声明,以下内容似乎有点笨拙:

class MyScala {
  var mySet: scala.collection.Set[String] = null 

  def init(): Unit = {
     val s = scala.collection.mutable.Set.empty[String]
     s + "Hello"
     s + "World"
     mySet = scala.collection.immutable.Set(s : _ *)

  }
}

尽管可以说这比Java版本更正确,因为不可变集合不能改变(如在Java情况下,底层集合可以在 unmodifiable 包装器下面改变)

4 回答

  • 3

    为什么在包scala中定义List和Seq而不是scala.collection(即使Seq的实现在集合子包中)?

    因为它们被认为非常有用,所以它们会通过scala.Predef中的同义词自动导入到所有程序中 .

    初始化集合然后冻结它的标准机制是什么(在Java中是通过以不可修改的形式包装来实现的)?

    Java没有冻结集合的机制 . 它只有一个成语,用于将(仍然可修改的)集合包装在一个抛出异常的包装器中 . Scala中正确的习惯用法是将可变集合复制到不可变集合中 - 可能使用:_ *

    为什么有些集合类型(例如MultiMap)只被定义为可变? (没有不可变的MultiMap)?

    团队/社区还没有到达那里 . 2.7分支看到了一堆补充,2.8预计会有更多 .

    由于强制完整的包声明,以下似乎有点笨拙:

    Scala允许导入别名,所以它在这方面总是比Java简洁(参见例如java.util.Date和java.sql.Date - 使用两个强制一个完全限定)

    import scala.collection.{Set => ISet}
    import scala.collection.mutable.{Set => MSet}
    
    class MyScala {
      var mySet: ISet[String] = null 
    
      def init(): Unit = {
         val s = MSet.empty[String]
         s + "Hello"
         s + "World"
         mySet = Set(s : _ *)
      }
    }
    

    当然,你真的只是将init写为 def init() { mySet = Set("Hello", "World")} 并保存所有麻烦或更好但只是把它放在构造函数 var mySet : ISet[String] = Set("Hello", "World")

  • 7

    可变集合偶尔也很有用(尽管我同意你应该首先考虑不可变集合) . 如果使用它们,我倾向于写

    import scala.collection.mutable
    

    在文件的顶部,(例如):

    val cache = new mutable.HashMap[String, Int]
    

    在我的代码中 . 这意味着你只需要编写“mutable.HashMap”,而不是scala.collection.mutable.HashMap“ . 作为上面提到的评论员,您可以在导入中重新映射名称(例如,“import scala.collection.mutable . {HashMap => MMap}”),但是:

    • 我不想破坏这些名字,以便更清楚我正在使用哪些类,以及

    • 我很少使用'mutable',因为在我的源代码中使用“mutable.ClassName”并不是一个不适当的负担 .

    (另外,我是否可以回应'避免空值'评论 . 它使代码更加健壮和易于理解 . 我发现我甚至不必像你期望的那样使用Option . )

  • 4

    几个随机的想法:

    • 我从不使用 null ,我使用 Option ,然后会丢掉一个不错的错误 . 这种做法已经摆脱了机会,并迫使人们写出不错的错误 .

    • 除非你确实需要,否则尽量避免查看"mutable"内容 .

    所以,我对你的scala示例的基本看法是,你必须在以后初始化该集合

    class MyScala {
      private var lateBoundSet:Option[ Set[ String ] ] = None
      def mySet = lateBoundSet.getOrElse( error("You didn't call init!") )
    
      def init {
         lateBoundSet = Some( Set( "Hello", "World" ) )
      }
    }
    

    我最近在办公室里一直在流泪 . “无效是邪恶的!”

  • 26

    请注意,当前版本中的Scala集合API可能存在一些不一致之处;对于Scala 2.8(将于2009年晚些时候发布),集合API正在进行大修,以使其更加一致和灵活 .

    在Scala网站上查看此文章:http://www.scala-lang.org/node/2060

    使用lateBoundSet添加到Tristan Juricek的示例:Scala有一个内置的懒惰机制初始化,使用“lazy”关键字:

    class MyClass {
        lazy val mySet = Set("Hello", "World")
    }
    

    通过这样做,mySet将在首次使用时初始化,而不是在创建新的MyClass实例时立即初始化 .

相关问题