Unetway

Swift - Типы коллекций

Swift предоставляет три основных типа коллекций , известных как массивы, наборы и словари, для хранения коллекций значений. Массивы - это упорядоченные коллекции значений. Наборы являются неупорядоченными коллекциями уникальных значений. Словари - это неупорядоченные коллекции ассоциаций ключ-значение.

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

ЗАМЕТКА

Типы массива, набора и словаря Swift реализованы в виде общих коллекций . Для получения дополнительной информации о родовых типах и коллекциях см. Обобщения .

Изменчивость коллекций

Если вы создаете массив, набор или словарь и присваиваете его переменной, созданная коллекция будет изменчивой . Это означает , что вы можете изменить (или мутируют ) коллекцию после того, как она создается путем добавления, удаления или изменения элементов в коллекции. Если вы назначаете константу массив, набор или словарь, эта коллекция является неизменной , и ее размер и содержимое не могут быть изменены.

ЗАМЕТКА

Хорошей практикой является создание неизменных коллекций во всех случаях, когда коллекция не нуждается в изменении. Это облегчит вам анализ вашего кода и позволит компилятору Swift оптимизировать производительность создаваемых вами коллекций.

Массивы

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

ЗАМЕТКА

ArrayТип Swift соединен с NSArrayклассом Foundation .

Для получения дополнительной информации об использовании Arrayс Фондом и Какао посмотрите Соединение между Массивом и NSArray .

Сокращенный синтаксис типа массива

Тип массива Swift записывается полностью как Array<Element>, где Element- тип значений, которые массиву разрешено хранить. Вы также можете записать тип массива в сокращенной форме как [Element]. Хотя эти две формы функционально идентичны, сокращенная форма является предпочтительной и используется в этом руководстве при обращении к типу массива.

Создание пустого массива

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

var someInts = [Int]()

print("someInts is of type [Int] with \(someInts.count) items.")

// Prints "someInts is of type [Int] with 0 items."

Обратите внимание, что тип someIntsпеременной выводится [Int]из типа инициализатора.

В качестве альтернативы, если контекст уже предоставляет информацию о типе, такую ​​как аргумент функции или уже типизированная переменная или константа, вы можете создать пустой массив с литералом пустого массива, который записывается как [](пустая пара квадратных скобок):

someInts.append(3)

// someInts now contains 1 value of type Int

someInts = []

// someInts is now an empty array, but is still of type [Int]

Создание массива со значением по умолчанию

ArrayТип Swift также предоставляет инициализатор для создания массива определенного размера со всеми его значениями, установленными на одно и то же значение по умолчанию. Вы передаете этому инициализатору значение по умолчанию соответствующего типа (вызываемый repeating): и сколько раз это значение повторяется в новом массиве (вызываемый count):

var threeDoubles = Array(repeating: 0.0, count: 3)

// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]

Создание массива путем добавления двух массивов вместе

Вы можете создать новый массив, сложив вместе два существующих массива с совместимыми типами с помощью оператора сложения ( +). Тип нового массива определяется по типу двух массивов, которые вы добавляете вместе:

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)

// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]



var sixDoubles = threeDoubles + anotherThreeDoubles

// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

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

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

[value 1, value 2, value 3]

В приведенном ниже примере создается массив, вызываемый shoppingListдля хранения Stringзначений:

var shoppingList: [String] = ["Eggs", "Milk"]

// shoppingList has been initialized with two initial items

shoppingListПеременная объявлена как «массив строковых значений», написанный в [String]. Поскольку в этом конкретном массиве указан тип значения String, разрешено хранить Stringтолько значения. Здесь shoppingListмассив инициализируется двумя Stringзначениями ( "Eggs"и "Milk"), записанными в литерале массива.

ЗАМЕТКА

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

В этом случае литерал массива содержит два Stringзначения и ничего больше. Это соответствует типу shoppingListобъявления переменной (массив, который может содержать только Stringзначения), и поэтому присваивание литерала массива разрешено как способ инициализации shoppingListс двумя начальными элементами.

Благодаря выводу типа Swift вам не нужно писать тип массива, если вы инициализируете его литералом массива, содержащим значения того же типа. Инициализация shoppingListмогла бы быть написана в более короткой форме:

var shoppingList = ["Eggs", "Milk"]

Поскольку все значения в литерале массива имеют один и тот же тип, Swift может сделать вывод, что [String]это правильный тип для использования с shoppingListпеременной.

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

Вы получаете доступ к массиву и изменяете его через методы и свойства или с помощью синтаксиса нижнего индекса.

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

print("The shopping list contains \(shoppingList.count) items.")

// Prints "The shopping list contains 2 items."

Используйте логическое isEmptyсвойство в качестве ярлыка для проверки, countравно ли свойство 0:

if shoppingList.isEmpty {

print("The shopping list is empty.")

} else {

print("The shopping list is not empty.")

}

// Prints "The shopping list is not empty."

Вы можете добавить новый элемент в конец массива, вызвав метод массива append(_:):

shoppingList.append("Flour")

// shoppingList now contains 3 items, and someone is making pancakes

В качестве альтернативы добавьте массив из одного или нескольких совместимых элементов с помощью оператора присваивания сложения ( +=):

shoppingList += ["Baking Powder"]

// shoppingList now contains 4 items

shoppingList += ["Chocolate Spread", "Cheese", "Butter"]

// shoppingList now contains 7 items

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

var firstItem = shoppingList[0]

// firstItem is equal to "Eggs"

ЗАМЕТКА

Первый элемент в массиве имеет индекс 0, а не 1. Массивы в Swift всегда индексируются нулями.

Вы можете использовать синтаксис нижнего индекса, чтобы изменить существующее значение по заданному индексу:

shoppingList[0] = "Six eggs"

// the first item in the list is now equal to "Six eggs" rather than "Eggs"

Когда вы используете синтаксис нижнего индекса, указанный вами индекс должен быть действительным. Например, запись для добавления элемента в конец массива приводит к ошибке во время выполнения.shoppingList[shoppingList.count] = "Salt"

Вы также можете использовать синтаксис нижнего индекса для одновременного изменения диапазона значений, даже если замещающий набор значений имеет длину, отличную от заменяемого диапазона. Следующий пример заменяет , и с и :"Chocolate Spread""Cheese""Butter""Bananas""Apples"

shoppingList[4...6] = ["Bananas", "Apples"]

// shoppingList now contains 6 items

Чтобы вставить элемент в массив по указанному индексу, вызовите метод массива insert(_:at:):

shoppingList.insert("Maple Syrup", at: 0)

// shoppingList now contains 7 items

// "Maple Syrup" is now the first item in the list

Этот вызов insert(_:at:)метода вставляет новый элемент со значением в самом начале списка покупок, обозначенным индексом ."Maple Syrup"0

Точно так же вы удаляете элемент из массива remove(at:)методом. Этот метод удаляет элемент по указанному индексу и возвращает удаленный элемент (хотя вы можете игнорировать возвращенное значение, если оно вам не нужно):

let mapleSyrup = shoppingList.remove(at: 0)

// the item that was at index 0 has just been removed

// shoppingList now contains 6 items, and no Maple Syrup

// the mapleSyrup constant is now equal to the removed "Maple Syrup" string

ЗАМЕТКА

Если вы попытаетесь получить доступ или изменить значение для индекса, который находится за пределами существующих границ массива, вы вызовете ошибку времени выполнения. Вы можете проверить правильность индекса перед его использованием, сравнив его со countсвойством массива . Наибольший допустимый индекс в массиве связан с тем, что массивы индексируются с нуля, однако, когда значение равно (это означает, что массив пуст), допустимых индексов нет.count - 1count0

Любые пробелы в массиве закрываются при удалении элемента, и поэтому значение индекса 0снова равно :"Six eggs"

firstItem = shoppingList[0]

// firstItem is now equal to "Six eggs"

Если вы хотите удалить последний элемент из массива, используйте removeLast()метод, а не remove(at:)метод, чтобы избежать необходимости запрашивать countсвойство массива . Как и remove(at:)метод, removeLast()возвращает удаленный элемент:

let apples = shoppingList.removeLast()

// the last item in the array has just been removed

// shoppingList now contains 5 items, and no apples

// the apples constant is now equal to the removed "Apples" string

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

Вы можете перебирать весь набор значений в массиве с помощью цикла forin:

for item in shoppingList {

print(item)

}

// Six eggs

// Milk

// Flour

// Baking Powder

// Bananas

Если вам нужен целочисленный индекс каждого элемента, а также его значение, используйте enumerated()метод для перебора массива. Для каждого элемента в массиве enumerated()метод возвращает кортеж, состоящий из целого числа и элемента. Целые числа начинаются с нуля и считаются на единицу для каждого элемента; если вы перечислите по всему массиву, эти целые числа соответствуют индексам элементов. Вы можете разложить кортеж на временные константы или переменные как часть итерации:

for (index, value) in shoppingList.enumerated() {

print("Item \(index + 1): \(value)")

}

// Item 1: Six eggs

// Item 2: Milk

// Item 3: Flour

// Item 4: Baking Powder

// Item 5: Bananas

Более о forinпетлях, см For-петли .

наборы

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

ЗАМЕТКА

SetТип Swift соединен с NSSetклассом Foundation .

Для получения дополнительной информации об использовании Setс Основой и Какао, посмотрите Соединение между Набором и NSSet .

Хеш-значения для типов набора

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

Все основных типов Свифта (например StringIntDouble, и Bool) являются hashable по умолчанию, и может быть использована в качестве типов заданного значения или словаря ключевых типов. Значения регистра в перечислении без связанных значений (как описано в перечислении ) также можно хэшировать по умолчанию.

ЗАМЕТКА

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

Поскольку Hashableпротокол соответствует Equatable, соответствующие типы также должны обеспечивать реализацию оператора equals ( ==). EquatableПротокол требует какой - либо соответствующей реализации , ==чтобы быть отношением эквивалентности. То есть, реализация ==должна удовлетворять следующим трем условиям, для всех значений abи c:

  • a == a (Рефлексивность)
  • a == bподразумевает (симметрия)b == a
  • a == b && b == cподразумевает (транзитивность)a == c

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

Установить синтаксис типа

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

Создание и инициализация пустого набора

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

var letters = Set<Character>()

print("letters is of type Set<Character> with \(letters.count) items.")

// Prints "letters is of type Set<Character> with 0 items."

ЗАМЕТКА

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

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

letters.insert("a")

// letters now contains 1 value of type Character

letters = []

// letters is now an empty set, but is still of type Set<Character>

Создание набора с литералом массива

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

В приведенном ниже примере создается набор favoriteGenresдля хранения Stringзначений:

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

// favoriteGenres has been initialized with three initial items

favoriteGenresПеременная объявлена как «совокупность Stringзначений», написанной в Set<String>. Поскольку этот конкретный набор указал тип значения из String, оно только разрешено хранить Stringзначение. Здесь, favoriteGenresмножество инициализируется с тремя Stringзначениями ( "Rock""Classical", и ), записанных в массиве буквального."Hip hop"

ЗАМЕТКА

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

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

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

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

Доступ и изменение набора

Вы получаете доступ и изменяете набор через его методы и свойства.

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

print("I have \(favoriteGenres.count) favorite music genres.")

// Prints "I have 3 favorite music genres."

Используйте логическое isEmptyсвойство в качестве ярлыка для проверки, countравно ли свойство 0:

if favoriteGenres.isEmpty {

print("As far as music goes, I'm not picky.")

} else {

print("I have particular music preferences.")

}

// Prints "I have particular music preferences."

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

favoriteGenres.insert("Jazz")

// favoriteGenres now contains 4 items

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

if let removedGenre = favoriteGenres.remove("Rock") {

print("\(removedGenre)? I'm over it.")

} else {

print("I never much cared for that.")

}

// Prints "Rock? I'm over it."

Чтобы проверить, содержит ли набор определенный элемент, используйте contains(_:)метод.

if favoriteGenres.contains("Funk") {

print("I get up on the good foot.")

} else {

print("It's too funky in here.")

}

// Prints "It's too funky in here."

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

Вы можете перебирать значения в наборе с помощью цикла forin.

for genre in favoriteGenres {

print("\(genre)")

}

// Classical

// Jazz

// Hip hop

Более о forinпетлях, см For-петли .

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

for genre in favoriteGenres.sorted() {

print("\(genre)")

}

// Classical

// Hip hop

// Jazz

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

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

Основные операции над множествами

На рисунке ниже изображены два набора - aи b- с результатами различных операций набора, представленных заштрихованными областями.

  • Используйте intersection(_:)метод, чтобы создать новый набор только со значениями, общими для обоих наборов.
  • Используйте symmetricDifference(_:)метод для создания нового набора со значениями в любом наборе, но не в обоих.
  • Используйте union(_:)метод для создания нового набора со всеми значениями в обоих наборах.
  • Используйте 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]

Установить членство и равенство

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

  • Используйте оператор «равно» ( ==), чтобы определить, содержат ли два набора все одинаковые значения.
  • Используйте isSubset(of:)метод, чтобы определить, содержатся ли все значения набора в указанном наборе.
  • Используйте isSuperset(of:)метод, чтобы определить, содержит ли набор все значения в указанном наборе.
  • Используйте методы isStrictSubset(of:)или, isStrictSuperset(of:)чтобы определить, является ли набор подмножеством или надмножеством, но не равным указанному набору.
  • Используйте isDisjoint(with:)метод, чтобы определить, не имеют ли два набора общих значений.
let houseAnimals: Set = ["