Работаем с 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 > Collection.toEnumSet(): EnumSet {
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 = rolesList.toEnumSet()
}
Преобразование произвольной коллекции в EnumSet
В предыдущем примере персоны сначала преобразуются в List
Функция помощник
inline fun > Iterable.mapToEnumSet(
crossinline transform: (T) -> R
): EnumSet = mapTo(EnumSet.noneOf(R::class.java), transform)
Пример использования
@Test
fun `get all unique roles`() {
val team = createPersons()
val allPersonRoles: Set = team
.filter { it.age > 40 }
.mapToEnumSet { it.type }
}
Группировка по типу в EnumMap
Функция помощник
inline fun > Iterable.groupByToEnumMap(
crossinline selector: (T) -> R
): EnumMap> {
return groupByTo(EnumMap(R::class.java), selector)
}
Пример использования
@Test
fun `group persons by role`() {
val team = createPersons()
val personsByRole: Map> = team
.groupByToEnumMap { it.type }
}
Группировка по типу в EnumMap с преобразованием
Функция помощник
inline fun , U> Iterable.groupByToEnumMap(
crossinline selector: (T) -> R,
crossinline transform: (T) -> U,
): EnumMap> {
return groupByTo(EnumMap(R::class.java), selector, transform)
}
Пример использования
@Test
fun `group person names by role`() {
val team = createPersons()
val namesByRole: Map> = team
.groupByToEnumMap({ it.type }) { it.name }
}
Ассоциирование по типу в EnumMap
Функция помощник
inline fun > Iterable.associateByToEnumMap(
crossinline selector: (T) -> R
): EnumMap {
return associateByTo(EnumMap(R::class.java), selector)
}
Пример использования
@Test
fun `associate by role`() {
val team = createPersons()
val roleToPersonMap: Map = team
.associateByToEnumMap { it.type }
}