Scala коллекция стандартной практики
Исходя из 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
(а такжеMap
,Stack
но нетList
)scala.collection.mutable.Set
(а такжеMap
,Buffer
но нетList
)scala.collection.jcl
Так что вопросы!
- Почему
List
а такжеSeq
определено в упаковке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 ответа
Почему 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")
Изменяемые коллекции иногда полезны (хотя я согласен, что вы всегда должны сначала смотреть на неизменяемые коллекции). Если использую их, я склонен писать
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 так, как вы ожидаете).
Пара случайных мыслей:
- Я никогда не пользуюсь
null
, Я используюOption
, который тогда бросил бы приличную ошибку. Эта практика избавилась от тонныNullPointerException
возможности и заставляет людей писать приличные ошибки. - Старайтесь избегать "изменчивых" вещей, если вам это действительно не нужно.
Итак, мой основной пример вашего примера с 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" ) )
}
}
Я недавно был в слезах вокруг офиса. "ноль это зло!"
Обратите внимание, что в текущей версии 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.