Scala: Могу ли я рассчитывать на порядок предметов в наборе? - PullRequest
30 голосов
/ 09 марта 2011

Это был довольно неприятный сюрприз:

scala> Set(1, 2, 3, 4, 5)       
res18: scala.collection.immutable.Set[Int] = Set(4, 5, 1, 2, 3)
scala> Set(1, 2, 3, 4, 5).toList
res25: List[Int] = List(5, 1, 2, 3, 4)

Пример сам по себе предлагает ответ «нет» на мой вопрос.Тогда как насчет ListSet?

scala> import scala.collection.immutable.ListSet
scala> ListSet(1, 2, 3, 4, 5)
res21: scala.collection.immutable.ListSet[Int] = Set(1, 2, 3, 4, 5)

Это, кажется, работает, но я должен полагаться на это поведение?Какая другая структура данных подходит для неизменной коллекции уникальных предметов, в которой должен быть сохранен первоначальный порядок?

Кстати, я знаю о distict методе в List.Проблема в том, что я хочу обеспечить уникальность элементов (при сохранении порядка) на уровне интерфейса, поэтому использование distinct испортит мой аккуратный дизайн.

EDIT

ListSet тоже не кажется надежным:

scala> ListSet(1, 2, 3, 4, 5).toList
res28: List[Int] = List(5, 4, 3, 2, 1)

EDIT2

В поисках идеального дизайна я попробовал это:

scala> class MyList[A](list: List[A]) { val values = list.distinct }
scala> implicit def toMyList[A](l: List[A]) = new MyList(l)
scala> implicit def fromMyList[A](l: MyList[A]) = l.values     

Что на самом деле работает:

scala> val l1: MyList[Int] = List(1, 2, 3)
scala> l1.values
res0: List[Int] = List(1, 2, 3)

scala> val l2: List[Int] = new MyList(List(1, 2, 3))
l2: List[Int] = List(1, 2, 3)

Однако проблема в том, что я не хочу показывать MyList вне библиотеки.Есть ли способ получить неявное преобразование при переопределении?Например:

trait T { def l: MyList[_] }
object O extends T { val l: MyList[_] = List(1, 2, 3) }
scala> O.l mkString(" ")  // Let's test the implicit conversion
res7: String = 1 2 3      

Я бы хотел сделать это так:

object O extends T { val l = List(1, 2, 3) }  // Doesn't work

Ответы [ 3 ]

45 голосов
/ 09 марта 2011

Это зависит от набора, который вы используете. Если вы не знаете, какая реализация Set у вас есть, тогда ответ прост: нет, вы не можете быть уверены. На практике я обычно сталкиваюсь со следующими тремя случаями:

  1. Мне нужно заказать предметы в наборе. Для этого я использую смешивание классов с чертой SortedSet, которая при использовании только стандартного API Scala всегда равна TreeSet. Это гарантирует, что элементы упорядочены в соответствии с их методом compareTo (см. Трату Ordered). Вы получаете (очень) небольшое снижение производительности для сортировки, поскольку время выполнения операций вставки / извлечения теперь является логарифмическим, а не (почти) постоянным, как в HashSet (при условии хорошей хэш-функции).

  2. Вам необходимо сохранить порядок, в котором элементы вставлены. Тогда вы используете LinkedHashSet. Практически так же быстро, как и обычный HashSet, требуется немного больше места для хранения дополнительных ссылок между элементами.

  3. Вам нет дела до порядка в наборе. Таким образом, вы используете HashSet. (Это значение по умолчанию при использовании метода Set.apply, как в первом примере)

Все это относится и к Java, Java имеет TreeSet, LinkedHashSet и HashSet и соответствующие интерфейсы SortedSet, Comparable и обычный Set.

13 голосов
/ 09 марта 2011

Я считаю, что вы никогда не должны полагаться на порядок в наборе.Ни на одном языке.

Помимо этого, посмотрите на этот вопрос , в котором подробно об этом говорится.

12 голосов
/ 09 марта 2011

ListSet всегда будет возвращать элементы в обратном порядке вставки, потому что оно поддерживается List, и оптимальный способ добавления элементов в List - это их предваряющий.

Неизменяемые структуры данных проблематичны, если вы хотите сначала войти, первым выйти (очередь). Вы можете получить O(logn) или амортизироваться O(1). Учитывая очевидную необходимость построить набор и затем создать из него итератор (т. Е. Сначала вы поместите все элементы, а затем удалите все элементы), я не вижу способа его амортизировать.

Вы можете полагать, что ListSet всегда будет возвращать элементы в порядке «последним вошел, первым вышел» (стек). Если этого достаточно, тогда сделайте это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...