Работаем с enum в kotlin/jvm правильно

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

А вы знали, что HashMap для enum уступает по эффективности EnumMap? Или что EnumSet под капотом это обычный long? Под катом несколько рецептов удобного применения этих структур.


Классы над которыми будем ставить эксперименты
    enum class RoleType {
        ACTOR,
        COMMENTATOR,
        VOICE_ACTOR,
        DIRECTOR,
        PRODUCER,
        SINGER,
        COMPOSER,
    }

    data class Person(
        val name: String,
        val type: RoleType,
        val age: Int,
    )

Преимущества EnumSet

Типовые операций (add, remove, contains, next) - реализованы при помощи битовых операций, они очень быстрые и выполняются за константное время.

Для енумов с размером меньше 64, типовая структура данных - long

Для енумов с размером больше 64, типовая структура данных - массив long

По сравнению с HashSet занимает гораздо меньший объем памяти

Неудобства EnumSet

По умолчанию EnumSet предоставляет достаточно неудобные статические методы для создания экземпляра класса EnumSet. Например для EnumSet.copyOf(otherCollection), otherCollection обязательно должна быть не пустой, иначе бросится исключение. Чтобы создать пустой EnumSet, надо применить громоздкую конструкцию EnumSet.noneOf(RoleType::class.java).

Преобразование коллекции енумов в EnumSet

Функция помощник

inline fun <reified T : Enum<T>> Collection<T>.toEnumSet(): EnumSet<T> {
    return if (this.isEmpty())
        EnumSet.noneOf(T::class.java)
    else
        EnumSet.copyOf(this)
}

Пример использования

@Test
fun `map any collection to enumSet`() {
    val rolesList = createPersons().map { it.type }
    val allPersonRoles: Set<RoleType> = rolesList.toEnumSet()
}

Преобразование произвольной коллекции в EnumSet

В предыдущем примере персоны сначала преобразуются в List<PersonType>, попробуем убрать этот промежуточный шаг

Функция помощник

inline fun <T, reified R : Enum<R>> Iterable<T>.mapToEnumSet(
  crossinline transform: (T) -> R
): EnumSet<R> = mapTo(EnumSet.noneOf(R::class.java), transform)

Пример использования

@Test
fun `get all unique roles`() {
    val team = createPersons()
    val allPersonRoles: Set<RoleType> = team
        .filter { it.age > 40 }
        .mapToEnumSet { it.type }
}

Группировка по типу в EnumMap

Функция помощник

inline fun <T, reified R : Enum<R>> Iterable<T>.groupByToEnumMap(
    crossinline selector: (T) -> R
): EnumMap<R, MutableList<T>> {
    return groupByTo(EnumMap(R::class.java), selector)
}

Пример использования

@Test
fun `group persons by role`() {
    val team = createPersons()
    val personsByRole: Map<RoleType, List<Person>> = team
        .groupByToEnumMap { it.type }
}

Группировка по типу в EnumMap с преобразованием

Функция помощник

inline fun <T, reified R : Enum<R>, U> Iterable<T>.groupByToEnumMap(
    crossinline selector: (T) -> R,
    crossinline transform: (T) -> U,
): EnumMap<R, MutableList<U>> {
    return groupByTo(EnumMap(R::class.java), selector, transform)
}

Пример использования

@Test
fun `group person names by role`() {
  val team = createPersons()
  val namesByRole: Map<RoleType, List<String>> = team
    .groupByToEnumMap({ it.type }) { it.name }
}

Ассоциирование по типу в EnumMap

Функция помощник

inline fun <T, reified R : Enum<R>> Iterable<T>.associateByToEnumMap(
    crossinline selector: (T) -> R
): EnumMap<R, T> {
    return associateByTo(EnumMap(R::class.java), selector)
}

Пример использования

@Test
fun `associate by role`() {
  val team = createPersons()
  val roleToPersonMap: Map<RoleType, Person> = team
    .associateByToEnumMap { it.type }
}

Источник: https://habr.com/ru/articles/781730/


Интересные статьи

Интересные статьи

Продолжаю мониторить ситуацию в технологическом секторе. Расскажу, что изменилось за последний месяц. (Еще есть обзоры по западным публикациям.)
Фавиконка — это иконка, которая отображается во вкладке браузера перед названием страницы, в закладках и на рабочем столе для веб-приложений.Фавиконки — ответственность верстальщика. Под катом рассказ...
Доброго всем времени суток! С вами я, Анна Жаркова, ведущий мобильный разработчик компании «Usetech». В предыдущей статье я рассказывала про один из способов реализации многопоточности в...
Обнаружил секретный репозиторий на гитхабе JetBrains под названием Projector. Благодаря нему написал кусок кода в IntelliJ IDEA, запущенной на Android-планшете. Рассказываю, как это п...
Вокруг IPv6 много заблуждений и мифов. Часто хостинг-провайдеры неправильно понимают, как его использовать и размышляют устаревшими подходами из мира IPv4. Например, имея октиллионы IPv6-адре...