Видеокурсы по изучению языка программирования Swift. Подробнее

Множества

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

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

Заметка

Тип Swift Set связан с классом Foundation NSSet.

Получить больше информации об использовании множества с фреймворками Foundation и Cocoa можно в Working with Cocoa Data Types в Using Swift with Cocoa and Objective-C (Swift 4).

Хеш значения для типа Set

Тип должен быть хешируемым для того, чтобы мог храниться в множестве, таким образом тип должен предоставлять возможность для вычисления собственного значения хеша. Тип значения хеша Int должен быть для всех объектов одинаковым, чтобы можно было провести сравнение что если a == b, то и a.hashValue == b.hashValue.

Все базовые типы Swift (Int, String, Double, Bool) являются хешируемыми типами по умолчанию и могут быть использованы в качестве типов значений множества или в качестве типов ключей словаря. Значения членов перечисления без каких-либо связанных значений (что описано в главе Перечисления) так же являются хешируемыми по умолчанию.

Заметка

Вы можете использовать ваш собственный тип в качестве типа значения множества или типа ключа словаря, подписав его под протокол Hashable из стандартной библиотеки Swift. Типы, которые подписаны под протокол Hashable должны обеспечивать gettable свойство hashValue. Значение, которое возвращает hashValue не обязательно должно иметь одно и то же значение при выполнении одной и той же программы или разных программ.

Так как протокол Hashable подписан под протокол Equatable, то подписанные под него типы так же должны предоставлять реализацию оператора равенства ==. Протокол Equatable требует любую реализацию оператора равенства для реализации возможности сравнения. Таким образом, реализация оператора == должна удовлетворять следующим трем условиям, для всех трех значений a, b, c.

  1. a == a (Рефлексивность)
  2. a == b, значит b == a (Симметрия)
  3. b == a && b == c, значит a == c (Транзитивность)

Для более подробной информации читайте главу "Протоколы".

Синтаксис типа множества

Тип множества Swift записывается как Set<Element>, Element является типом, который храниться в множестве. В отличии от массивов множества не имеют сокращенной формы записи.

Создание и инициалиазация пустого множества

Вы можете создать пустое множество конкретного типа, используя синтаксис инициализатора:

var letters = Set<Character>()
print("letters имеет тип Set<Character> с \(letters.count) элементами.")
// Выведет "letters имеет тип Set<Character> с 0 элементов."

Заметка

Тип переменной letters выведен из типа инициализатора как Set<Character>.

Альтернативно, если контекст предоставляет информацию о типе, например как аргумент функции или просто явное указание типа переменной или константы, то вы можете создать пустое множество при помощи пустого литерала массива:

letters.insert("a")
// letters теперь содержит 1 элемент типа Character
letters = []
// letters теперь является пустым множеством, но все еще имеет тип Set<Character>

Создание множества при помощи литерала массива

Вы так же можете инициализировать множество при помощи литерала массива, чтобы использовать его в качестве сокращенной записи нескольких элементов в качестве коллекции множества.

Пример ниже создает множество favoriteGenres для хранения String.

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres был инициализирован при помощи трех начальных элементов

Переменная favoriteGenres объявлена как множество значений типа String, который записывается как Set<String>. Так как это множество имеет определенный тип String, то этому множеству позволено хранить только значения типа String. Поэтому здесь мы инициализируем favoriteGenres тремя значениями типа String, записанными в виде литерала массива.

Заметка

Множество favoriteGenres объявлен как переменная (ключевое слово var), но не константа (ключевое слово let), так как мы добавляем и удаляем элементы в примере ниже.

Так как тип множества не может быть выведен только из литерала, то его тип должен быть указан явно. Однако из-за вывода типа в Swift вы не должны писать тип множества, если вы инициализируете его при помощи литерала массива, который содержит элементы одного типа. Вместо этого инициализация favoriteGenres может быть записана и в более короткой форме:

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

Так как все элементы литерала массива одного типа, то Swift может вывести, что Set<String> является корректным типом для переменной favoriteGenres.

Доступ и изменение множества

Получить доступ и модифицировать множества можно через свойства и методы.

Для того, чтобы выяснить количество элементов в множестве вам нужно использовать свойство count:

print("У меня есть \(favoriteGenres.count) любимых музыкальных жанра.")
// Выведет "У меня есть 3 любимых музыкальных жанра."

Используйте булево свойство isEmpty в качестве сокращенной проверки наличия элементов во множестве или другими словами равно ли свойство count 0:

if favoriteGenres.isEmpty {
    print("Мне все равно какая музыка играет. Я не придирчив.")
} else {
    print("У меня есть свои музыкальные предпочтения.")
}
// Выведет "У меня есть свои музыкальные предпочтения."

Вы можете добавить новый элемент во множество, используя метод insert(_:):

favoriteGenres.insert("Jazz")
// теперь в favoriteGenres находится 4 элемента

Вы так же можете удалить элемент из множества, используя метод remove(_:), который удаляет элемент, который является членом множества и возвращает удаленное значение или nil, если удаляемого элемента нет. Так же все объекты множества могут быть удалены единовременно при помощи метода removeAll().

if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? С меня хватит.")
} else {
    print("Меня это не сильно заботит.")
}
// Выведет "Rock? С меня хватит."

Можно проверить наличие определенного элемента во множестве, используя метод contains(_:):

if favoriteGenres.contains("Funk") {
    print("О! Да я встал с правильной ноги!")
} else {
    print("Слишком много Funk'а тут.")
}
// Выведет "Слишком много Funk'а тут."

Итерация по множеству

Вы можете совершать итерации по множеству при помощи цикла for-in.

for genre in favoriteGenres {
    print("\(genre)")
}
// Classical
// Jazz
// Hip hop

Для более подробной информации по циклу for-in читайте в главе Циклы For-in.

Множества в Swift не имеют определенного порядка. Для того, чтобы провести итерацию по множеству в определенном порядке, вам нужно использовать метод sorted(), который возвращает вам элементы коллекции в виде отсортированного массива, используя оператор <.

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical
// Hip hop
// Jazz

Выполнение операций с множествами

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

Базовые операции множеств

Иллюстрации внизу изображают два множества a и b в результате применения различных методов.

  1. Используйте метод intersection(_:) для создания нового множества из общих значений двух входных множеств.
  2. Используйте метод symmetricDifference(_:) для создания нового множества из значений, которые не повторяются в двух входных множествах.
  3. Используйте метод union (_:) для создания нового множества состоящего из всех значений обоих множеств.
  4. Используйте метод subtracting (_:) для создания множества со значениями не принадлежащих указанному множеству из двух входных.
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
 
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

Взаимосвязь и равенство множеств

Иллюстрация ниже отображает три множества a, b и c. Множество a является надмножеством множества b, так как содержит все его элементы, соответственно множество b является подмножеством множества a, опять таки потому, что все его элементы находятся в a. Множества b и c называются разделенными, так как у них нет общих элементов.

  1. Используйте оператор равенства (==) для определения все ли значения двух множеств одинаковы.
  2. Используйте метод isSubset(of:) для определения все ли значения множества содержаться в указанном множестве.
  3. Используйте метод isSuperset(of:), чтобы определить содержит ли сет все значения указанного сета.
  4. Используйте методы isStrictSubset(of:) или isStrictSuperset(of:) для определения является ли множество подмножеством или надмножеством, но не равным указанному сету.
  5. Используйте метод isDisjoint(with:) для определения того, есть ли общие значения в двух множествах или нет.
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
 
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

 

<– Массивы

Swift: 
4.0