Урок 2.6 Коллекции

До сих пор в этом курсе вы учились работать с отдельными элементами, будь то константы или переменные. Но иногда вы обнаружите, что имеет больше смысла работать с группой объектов в целом. Коллекция позволяет вам ссылаться на несколько объектов одновременно.

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


Что Вы Узнаете

  • Как объявить коллекции констант и переменных
  • Как добавлять и удалять значения из массивов и словарей
  • Как выбрать подходящий тип коллекции

Терминология


Связанные Ресурсы

Руководство по языку программирования Swift: Типы коллекций

 

Swift определяет два типа коллекций, с которыми вы часто будете работать: массивы и словари. Каждый тип предоставляет уникальный метод взаимодействия с несколькими объектами. По мере изучения различных типов вы заметите, что они обладают определенной функциональностью: добавление/удаление элементов, доступ к отдельным элементам и предоставление информации о типе данных в коллекции.

Массивы

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

Массив часто инициализируется с помощью литерала, аналогичного Int или строковому литералу. Чтобы объявить литерал массива, заключите набор значений в квадратные скобки, разделяя значения запятыми:

[value1, value2, value3]

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

var names: [String] = [”Anne”, “Gary”, “Keith”]

Поскольку вывод типа может определить, что “Anne”, “Gary” и “Keith” являются строками, вы можете фактически пропустить шаг и объявить массив без указания типа массива:

var names = [”Anne”, “Gary”, “Keith”]

Но могут возникнуть ситуации, когда вы захотите указать тип массива, даже если вывод типа может его обнаружить. Представьте, например, что вам нужна коллекция 8-разрядных целых чисел (числа от -127 до 128). Вы начинаете с присвоения переменной следующему набору чисел:

var numbers = [1, -3, 50, 72, -95, 115]

Swift выведет, что все значения имеют тип Int, и установит, что numbers представляют собой массив целых чисел или [Int]. Хотя этот вывод, безусловно, верен, существует проблема: Int может содержать положительные и отрицательные числа, выходящие за пределы диапазона от -127 до 128. Чтобы помочь Swift понять, что вы хотите ограничить массив меньшими целыми числами, вы можете указать тип как [Int8], массив 8-разрядных целых чисел, значения которых ограничены вышеупомянутым диапазоном:

var numbers: [Int8] = [1, -3, 50, 72, -95, 115]

Что, если вы попытаетесь включить большее число, например 300, в литерал массива типа [Int8]? Swift вернет ошибку, поскольку 300 больше максимального числа для Int8. Вы должны указать тип, если предполагаемый тип недостаточно специфичен, и это поможет ограничить значения, которые вы не хотите разрешать.

Часто бывает полезно проверить, существует ли определенное значение в массиве. Для этого вы можете использовать метод contains(_:), передавая нужное значение. Если массив содержит значение, выражение равно true; в противном случае оно равно false:

let numbers = [4, 5, 6]
if numbers.contains(5) {
  print(”There is a 5”)
}

В начале этого курса вы узнали, что константы не могут быть изменены после их объявления. То же самое происходит, когда вы присваиваете коллекции константу с помощью let: вы не можете добавлять, удалять или изменять какие-либо элементы в коллекции. Однако, если вы храните коллекцию в переменной, используя var, вы сможете добавлять, удалять или изменять элементы в коллекции. Кроме того, вы сможете очистить коллекцию или установить переменную в совершенно другую коллекцию.

Типы массивов

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

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

В этом примере определяется массив с традиционной аннотацией типа.

var myArray: [Int] = []

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

var myArray: Array<Int> = []

Точно так же, как все объекты можно инициализировать, добавив a () после имени типа, вы можете добавить a () после [Int], чтобы инициализировать пустой массив объектов Int. Вы также должны быть знакомы с этим кодом на случай, если столкнетесь с ним в будущем.

var myArray = [Int]()

Работа С Массивами

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

Все в порядке. У Swift есть решение. Вместо подсчета 100 нулей вы можете использовать следующий инициализатор для создания массива count 100 с повторяющимися значениями по умолчанию:

var myArray = [Int](repeating: 0, count: 100)

Чтобы узнать количество элементов в массиве, вы можете использовать его свойство count. Что делать, если вы хотите проверить, пуст ли массив? Вместо того, чтобы сравнивать count с 0, вы можете проверить свойство isEmpty массива, которое возвращает Bool:

let count = myArray.count
if myArray.isEmpty { }

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

let firstName = names[0]

Вы также можете использовать синтаксис подстрочного индекса для изменения значения по заданному индексу. Поместив нижний индекс слева от знака =, вы можете установить определенное значение, а не обращаться к текущему. В приведенном ниже примере вы меняете второе имя в коллекции на “Paul”

names[1] = “Paul”

При подписывании массива вы должны быть уверены, что указанный вами индекс существует в массиве. Например, если вы укажете 3 в качестве индекса, массив должен содержать не менее четырех элементов. Если этого не произойдет, ваша программа завершит работу при выполнении этой строки кода.

После определения массива и установки некоторых значений вам часто захочется добавить (append) новые значения. Вы можете использовать функцию добавления массива переменных, чтобы добавить новый элемент в конец массива. Чтобы добавить несколько элементов одновременно, используйте оператор +=.

var names = [”Amy”]
names.append(”Joe”)
names += [”Keith”, “Jane”]
print(names) // [”Amy”, “Joe”, “Keith”, “Jane”]

Что, если вы не хотите, чтобы новый элемент отображался в конце массива? Массив переменных также имеет функцию insert(_:at:), которая позволяет указать значение и индекс. В этом сценарии, как и в случае подписки, вам нужно быть уверенным, что вы указали действительный индекс.

Поскольку массив индексируется с нулевым индексом, первый элемент всегда имеет индекс 0, а индекс последнего элемента равен count – 1. В следующем примере “Bob” вставляется в начало массива:

names.insert(”Bob”, at: 0)

Аналогично, функция remove(at:) работает для удаления элемента по указанному индексу. В отличие от insert(_:at:), вам не нужно вводить значение; функция возвращает удаленный элемент по умолчанию, как в примере ниже. Другим методом является removeLast(), который удаляет последний элемент из массива, устраняя необходимость вычисления индекса. Наконец, метод removeAll() удалит все элементы из массива.

var names = [”Amy”, “Brad”, “Chelsea”, “Dan”]
let chelsea = names.remove(at: 2)
let dan = names.removeLast()
names.removeAll()

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

var myNewArray = firstArray + secondArray

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

let array1 = [1,2,3]
let array2 = [4,5,6]
let containerArray = [array1, array2] // [ [1,2,3], [4,5,6] ]
let firstArray = containerArray[0]  // [1,2,3]
let firstElement = containerArray[0][0] // 1

Словари

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

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

[key1: value1, key2: value2, key3: value3]

Так же, как и в случае с массивом, тип словаря может быть выведен на основе типов, используемых в литерале словаря. Допустим, вы хотите сохранить список рекордов в игре. Вы будете использовать имя игрока в качестве ключа, а счет игрока - в качестве соответствующего значения. Тип словаря будет выведен как словарь, где ключи имеют тип String, а значения - тип Int. Это может быть представлено либо как [String: Int], либо как Dictionary<String, Int>:

var scores = [”Richard”: 500, “Luke”: 400, “Cheryl”: 800]

Как и в случае с другими типами коллекций, словарь имеет свойство count для определения количества пар ключ-значение и свойство isEmpty для определения того, нет ли в словаре пар ключ-значение. Синтаксис для создания пустого словаря, вероятно, уже знаком:

var myDictionary = [String: Int]()
 
var myDictionary = Dictionary<String, Int>()
 
var myDictionary: [String: Int] = [:]

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

Добавление/Удаление/Изменение словаря

Синтаксис подстрочного индекса особенно удобен при работе со словарем. Поскольку порядок в словаре Swift не имеет значения, индекс отсутствует, и нет никакого риска ошибок, связанных с индексами.

Следующий пример добавляет новый балл в словарь рекордов. Если ключ “Oli” уже существует в словаре, этот код заменит старое значение новым:

scores[”Oli”] = 399

Но что, если вы хотите узнать, есть ли старое значение в словаре, прежде чем заменять его? Вы можете использовать updateValue(_:, forKey:) для обновления словаря, и значение, возвращаемое методом, будет равно oldValue, если таковое существовало. В следующем примере значение oldValue будет равно старому значению Ричарда до обновления. Если значения не было, то старое значение будет равно nil. Подробнее о ключевом слове nil вы узнаете позже. Это особый способ представления отсутствия значения.

let oldValue = scores.updateValue(100, forKey: “Richard”)

Swift использует синтаксис if-let, позволяющий запускать код только в том случае, если из метода возвращается значение. Если бы не существовало существующего значения, код в фигурных скобках не выполнялся бы:

if let oldValue = scores.updateValue(100, forKey: “Richard”) {
  print(”Richard’s old value was \(oldValue)”)
}

Чтобы удалить элемент из словаря, вы можете использовать синтаксис нижнего индекса, установив значение равным nil. Аналогично обновлению значения, в словарях есть метод removeValue(forKey:), если вам нужно вернуть старое значение перед его удалением:

var scores = [”Richard”: 100, “Luke”: 400, “Cheryl”: 800]
scores[”Richard”] = nil // [”Luke”: 400, “Cheryl”: 800]
 
if let removedValue = scores.removeValue(forKey: “Luke”) {
  print(”Luke’s score was \(removedValue) before he stopped 
  playing”)
}

Доступ к словарю

Словари Swift предоставляют два свойства, не включенные в другие типы коллекций. Вы можете использовать keys для возврата списка всех ключей в словаре, а values - для возврата списка всех значений. Если вы хотите использовать эти коллекции впоследствии, вам нужно будет преобразовать их в массивы:

var scores = [”Richard”: 500, “Luke”: 400, “Cheryl”: 800]
 
let players = Array(scores.keys) // [”Richard”, “Luke”, “Cheryl”]
let points = Array(scores.values) // [500, 400, 800]

Чтобы найти определенное значение в словаре, используйте синтаксис if-let. Если указанный вами ключ находится в словаре, результатом будет соответствующее значение ключа. Однако, если ключа нет в словаре, код в скобках выполняться не будет.

if let lukesScore = scores[”Luke”] {
  print(lukesScore)
}
 
if let henrysScore = scores[”Henry”] {
  print(henrysScore) // not executed; “Henry” is not a key in the dictionary
}


Console Output:
400

 

Лабораторная работа

Откройте и завершите упражнения в Lab—Collections.playground.

Подключение к дизайну

Откройте рабочую книгу по дизайну приложения и просмотрите раздел Карты для вашего приложения. Подумайте о том, какую информацию может потребоваться использовать вашему приложению. Есть ли в вашем приложении информация, для которой вам нужно будет использовать коллекцию, то есть где вам нужно будет ссылаться на несколько объектов одновременно? Добавьте комментарии в раздел карты или в новый пустой слайд в конце документа. Попробуйте определить информацию, для которой вам понадобится массив или словарь в вашем приложении.

В примере приложения Go Green для рабочей книги каждый раз, когда пользователь регистрирует элемент, он может создавать новый экземпляр структуры под названием "ItemEntry’. Новый экземпляр может быть добавлен в массив элементов, который отслеживает все записи журнала. 

 

 


Отрывок из книги
Develop in Swift Fundamentals
Apple Education
https://books.apple.com/ru/book/develop-in-swift-fundamentals/id1581182804

Information

Apple, the Apple logo, Apple Books, Apple TV, Apple Watch, Cocoa, Cocoa Touch, Finder, Handoff, HealthKit, iPad, iPad Pro, iPhone, iPod touch, Keynote, Mac, macOS, Numbers, Objective-C, Pages, Photo Booth, Safari, Siri, Spotlight, Swift, tvOS, watchOS, and Xcode are trademarks of Apple Inc., registered in the U.S. and other countries. App Store and iBooks Store are service marks of Apple Inc., registered in the U.S. and other countries. ​
The Bluetooth® word mark and logos are registered trademarks owned by Bluetooth SIG, Inc. and any use of such marks by Apple is under license. ​
IOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. ​
Other product and company names mentioned herein may be trademarks of their respective companies.