Преобразование изменяемой карты Seq в неизменяемую карту IndexedSeq в Scala

Мне нужно обработать большое количество записей (несколько миллионов), представляющих людей. Я хотел бы создать раздел по году рождения, а затем обрабатывать каждую группу отдельно. Я пытаюсь создать функциональное решение (без/минимальные изменяемые данные), чтобы оно было потокобезопасным и могло быть распараллелено.

В качестве первой попытки я создал хвостовую рекурсивную функцию, которая создает Map[Int, IndexedSeq], сопоставляющую каждый год рождения с последовательностью записей о людях. Мне нужна проиндексированная последовательность, потому что я буду выполнять произвольный доступ к людям в каждой группе. Вот мой код:

@tailrec
def loop(people: Seq[Person],
         map: Map[Int, IndexedSeq[Person]] = Map()): Map[Int, IndexedSeq[Person]] = {
  if (people.isEmpty) map
  else {
    val person = people.head
    val yearOfBirth = person.yearOfBirth
    val seq = map.getOrElse(yearOfBirth, IndexedSeq())
    loop(people.tail, map + (yearOfBirth -> (seq :+ person)))
  }
}

Это работает, но не очень эффективно. Я могу добиться большего успеха, разрешив небольшое количество очень локализованной изменчивости. Если все изменяемые переменные находятся в стеке, код по-прежнему будет потокобезопасным, пока вывод Map неизменен.

Я хотел бы реализовать это путем внутреннего создания изменяемого Map[Int, List[Person]], а затем эффективно преобразовать его в неизменяемый Map[Int, IndexedSeq[Person]] в качестве возвращаемого значения.

Как преобразовать изменяемые Map из List элементов в неизменяемые Map[Int, IndexedSeq[Person]] наиболее эффективным способом? Обратите внимание, что для людей в каждой группе года рождения нет определенного порядка.


person Ralph    schedule 13.03.2012    source источник
comment
Незначительный момент, вероятно, не имеющий значения: поскольку вы разбиваете по году рождения, AFAICT вы никогда не передадите саму карту за пределы этой темы.   -  person Ed Staub    schedule 13.03.2012


Ответы (1)


Почему бы вам не использовать groupByфункцию признака Seq? (документация находится здесь: http://www.scala-lang.org/api/current/index.html#scala.collection.Seq)

def groupByYearOfBirth(people: Seq[Person]) = people.groupBy(_.yearofBirth)

Изменить: вопреки моему первоначальному предложению, не используйте .mapValues(_.toIndexedSeq) to provide anIndexedSeq`. Дэниел объясняет, почему в комментарии ниже.

person Nicolas    schedule 13.03.2012
comment
Я не использовал вашу методику, потому что не знал о ней. :-) Похоже, он будет делать именно то, что мне нужно. Разве Scala не прекрасна?! Спасибо. - person Ralph; 13.03.2012
comment
Да, в scala всегда так: сначала внимательно посмотрите на API, в большинстве случаев оно содержит именно то, что вам нужно. ;) - person Nicolas; 13.03.2012
comment
НЕ используйте mapValues таким образом! Реализация mapValues работает как представление, что означает, что оно будет применять это преобразование каждый раз, когда вы обращаетесь к этому значению. Учитывая разницу между Seq и IndexedSeq, вполне вероятно, что этот способ ничего не даст. Вместо этого используйте обычный map, чтобы создать новый Map. - person Daniel C. Sobral; 13.03.2012
comment
Большое спасибо Даниил, я не знал, что он оценивает новое значение при каждом доступе. - person Nicolas; 13.03.2012