← Назад к вопросам

Как получить уникальные объекты в списке?

1.0 Junior🔥 161 комментариев
#Kotlin основы#Коллекции и структуры данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Получение уникальных объектов в списке на Android/Kotlin

Получение уникальных объектов из списка — распространённая задача, и подход зависит от того, что именно считается уникальностью. Рассмотрим основные методы.

1. Использование Set (множества) для примитивных типов или данных-классов

Для типов с автоматически генерируемыми equals() и hashCode() (примитивы, String, data-классы) можно использовать toSet() или distinct():

val numbers = listOf(1, 2, 2, 3, 4, 4, 5)

// Через distinct() - сохраняет порядок исходного списка
val uniqueNumbers1 = numbers.distinct() // [1, 2, 3, 4, 5]

// Через преобразование в Set - порядок не гарантируется
val uniqueNumbers2 = numbers.toSet() // [1, 2, 3, 4, 5]

2. Работа с пользовательскими классами

Для пользовательских классов необходимо определить, что делает объект уникальным. Рассмотрим пример:

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

val people = listOf(
    Person(1, "Анна", 25),
    Person(2, "Иван", 30),
    Person(1, "Анна", 25), // Дубликат по id
    Person(3, "Иван", 28)  // Другой человек с таким же именем
)

Вариант A: Уникальность по всем полям (стандартный для data-классов)

val uniquePeople = people.distinct() // Удалит полный дубликат Person(1, "Анна", 25)

Вариант B: Уникальность по конкретному полю/полям

// Уникальные по id
val uniqueById = people.distinctBy { it.id } // Оставит первого Person с id=1

// Уникальные по имени
val uniqueByName = people.distinctBy { it.name } // Оставит первого "Ивана"

// Уникальность по комбинации полей
val uniqueByNameAndAge = people.distinctBy { it.name to it.age }

3. Использование фильтрации с запоминанием состояния

Для более сложной логики можно использовать HashSet в комбинации с filter:

val seenIds = mutableSetOf<Int>()
val uniquePeople = people.filter { seenIds.add(it.id) } // Добавляет в set, возвращает true только для новых id

4. Производительность и особенности

  • distinct() — создает временный HashSet, поэтому сложность O(n) по памяти и времени. Сохраняет порядок элементов (использует LinkedHashSet внутри).
  • distinctBy() — аналогично, но вычисляет ключ для каждого элемента. Полезен, когда уникальность определяется не всем объектом, а его частью.
  • toSet() — простая конвертация, но не гарантирует сохранения порядка.

5. Для потоков данных (Flow, Sequence)

// Для Flow
peopleFlow.distinctUntilChanged()

// Для Sequence (ленивая обработка)
people.asSequence().distinct().toList()

6. Android-специфика: работа с Realm, Room или другими базами данных

При использовании локальных БД часто лучше решать задачу уникальности на уровне запроса:

// Room пример
@Query("SELECT DISTINCT name FROM users")
fun getUniqueUserNames(): List<String>

// Realm пример
realm.where<User>().distinct("id").findAll()

Рекомендации по выбору метода

  1. Для простых случаев — используйте distinct() или distinctBy()
  2. Когда порядок не важенtoSet() или toMutableSet()
  3. При работе с большими списками — рассмотрите asSequence().distinct() для ленивых вычислений
  4. Для кастомной логики — используйте filter с MutableSet
  5. В Android UI (RecyclerView) — часто используйте DiffUtil с уникальными идентификаторами вместо ручной фильтрации

Важно помнить, что уникальность определяется методами equals() и hashCode() объекта. Для пользовательских классов без data-модификатора нужно переопределять эти методы вручную или использовать distinctBy() с явным указанием ключа сравнения.

Как получить уникальные объекты в списке? | PrepBro