Стандартная коллекция Scala - PullRequest
31 голосов
/ 24 марта 2009

Исходя из 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.ListSeq)
  • scala.collections.SetMap)
  • scala.collection.immutable.SetMap, Stack, но не List)
  • scala.collection.mutable.SetMap, Buffer, но не List)
  • scala.collection.jcl

Так что вопросы!

  1. Почему List и Seq определены в пакете scala, а не scala.collection (хотя реализации Seq находятся в подпакетах коллекции)?
  2. Каков стандартный механизм инициализации коллекции, а затем замораживания ее (что в Java достигается путем переноса в unmodifiable)?
  3. Почему некоторые типы коллекций (например, MultiMap) определяются только как изменяемые? (Нет неизменного MultiMap)?

Я прочитал превосходную серию Даниэля Спивака о коллекциях scala и до сих пор озадачен тем, как на самом деле их можно использовать на практике. Следующее выглядит немного громоздким из-за принудительного объявления полного пакета:

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 ]

26 голосов
/ 24 марта 2009

Почему List и Seq определены в пакете scala, а не в 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 голосов
/ 19 ноября 2009

Изменяемые коллекции иногда полезны (хотя я согласен, что вы всегда должны сначала смотреть на неизменяемые коллекции). Если я использую их, я склонен писать

import scala.collection.mutable

вверху файла и (например):

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

в моем коде. Это означает, что вам нужно написать «mutable.HashMap», а не scala.collection.mutable.HashMap ». Как упоминалось выше, вы можете переназначить имя в импорте (например, «import scala.collection.mutable. {HashMap => MMap}»), но:

  1. Я предпочитаю не искажать имена, чтобы было понятнее, какие классы я использую, и
  2. Я использую 'mutable' достаточно редко, так что наличие в моем источнике 'mutable.ClassName' не является неоправданное бремя.

(Кроме того, могу ли я повторить комментарий «избегать нулей». Это делает код намного более надежным и понятным. Я считаю, что мне даже не нужно использовать Option так, как вы ожидаете.)

4 голосов
/ 06 июня 2009

Пара случайных мыслей:

  1. Я никогда не использую null, я использую Option, что затем выдает приличную ошибку. Эта практика избавилась от возможностей ton NullPointerException и вынуждает людей писать приличные ошибки.
  2. Старайтесь избегать "изменчивых" вещей, если вам это действительно не нужно.

Итак, мой основной пример вашего примера с 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" ) )
  }
}

Я недавно разрыдалась в офисе. «ноль - это зло!»

3 голосов
/ 03 августа 2009

Обратите внимание, что в текущей версии API коллекций Scala могут быть некоторые несоответствия; для Scala 2.8 (будет выпущен позднее в 2009 году) API-интерфейс коллекций пересматривается, чтобы сделать его более согласованным и более гибким.

См. Эту статью на веб-сайте Scala: http://www.scala -lang.org / node / 2060

Чтобы добавить пример Tristan Juricek с lateBoundSet: в Scala есть встроенный механизм для отложенной инициализации с использованием ключевого слова "lazy":

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

При этом mySet будет инициализирован при первом использовании, а не сразу при создании нового экземпляра MyClass.

...