侧边栏壁纸
  • 累计撰写 28 篇文章
  • 累计创建 10 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

100天学会SwiftUI——数组、字典、集合和枚举

Jserv
2025-05-06 / 0 评论 / 0 点赞 / 10 阅读 / 14685 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2025-05-06,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

如何在数组中存储有序数据

想要在一个地方拥有大量数据是非常常见的,无论是一周中的几天、班级的学生名单、过去100年的城市人口,还是无数其他的例子。

在Swift中,我们使用数进行分组。数组是它们自己的数据类型,就像StringIntDouble一样,但它们不仅包含一个字符串,它们可以包含零字符串、一个字符串、两个字符串、三个、五百万、五百万甚至更多的字符串——它们可以自动适应以包含您需要的字符串,并始终按照您添加的顺序保留数据。

让我们从一些创建数组的简单示例开始:

var beatles = ["John", "Paul", "George", "Ringo"]
let numbers = [4, 8, 15, 16, 23, 42]
var temperatures = [25.3, 28.2, 26.4]

这创造了三个不同的数组:一个包含人名的字符串,一个包含重要数字的整数,一个包含温度的十进制,单位为摄氏度。请注意,我们如何使用方括号开始和结束数组,每个项目之间都带有逗号。

当从数组中读取值,我们根据它们在数组中出现的位置来询问值。项目在数组中的位置通常称为其索引

这让初学者有点困惑,但Swift实际上从零开始计算项目的索引,而不是从一开始计算——例如,beatles[0]是第一个元素,beatles[1]是第二个元素。

因此,我们可以像这样从我们的数组中读取一些值:

print(beatles[0])
print(numbers[1])
print(temperatures[2])

提示:确保项目存在于您请求的索引中,否则您的代码将崩溃——您的应用程序将停止工作。

如果您的数组是可变的,您可以在创建后对其进行修改。例如,您可以使用append()来添加新项目:

beatles.append("Adrian")

没有什么能阻止你多次添加项目:

beatles.append("Allen")
beatles.append("Adrian")
beatles.append("Novall")
beatles.append("Vivian")

然而,Swift确实会监视您尝试添加的数据类型,并将确保您的数组一次只包含一种类型的数据。所以,这种代码是不允许的:

temperatures.append("Chris")

This also applies to reading data out of the array – Swift knows that the beatles array contains strings, so when you read one value out you’ll always get a string. If you try to do the same with numbers, you’ll always get an integer. Swift won’t let you mix these two different types together, so this kind of code isn’t allowed:

let firstBeatle = beatles[0]
let firstNumber = numbers[0]
let notAllowed = firstBeatle + firstNumber

这是类型安全,就像Swift不允许我们混合整数和小数一样,除了它被提升到更深层次。是的,所有beatlesnumbers都是数组,但它们是专门的数组类型:一个是字符串数组,一个是整数数组。

当您想从一个空数组开始并逐个添加项目时,您可以更清楚地看到这一点。这是用非常精确的语法完成的:

var scores = Array<Int>()
scores.append(100)
scores.append(80)
scores.append(85)
print(scores[1])

我们已经涵盖了最后四行,但第一行显示了我们如何拥有专门的数组类型——这不仅仅是任何数组,而是一个包含整数的数组。这就是让Swift确定的原因,beatles[0]必须始终是一个字符串,也是阻止我们将整数添加到字符串数组中的原因。

Array<Int>之后的开括号和闭括号是存在的,因为如果需要,可以自定义数组的创建方式。例如,在以后添加真实内容之前,您可能想用大量临时数据填充数组。

您可以通过以不同的方式进行专业化来制作其他类型的数组,如下:

var albums = Array<String>()
albums.append("Folklore")
albums.append("Fearless")
albums.append("Red")

再次,我们已经说过,必须始终包含字符串,所以我们不能尝试把整数放在那里。

数组在Swift中非常常见,以至于有一种特殊的方法来创建它们:您可以写[String],而不是写Array<String>。所以,这种代码和以前一模一样:

var albums = [String]()
albums.append("Folklore")
albums.append("Fearless")
albums.append("Red")

Swift的类型安全性意味着它必须始终知道数组存储的数据类型。这可能意味着通过说albumsArray<String>来明确,但如果你提供一些初始值,Swift可以自己弄清楚:

var albums = ["Folklore"]
albums.append("Fearless")
albums.append("Red")

在我们完成之前,我想提一下数组附带的一些有用功能。

首先,您可以使用.count来读取数组中有多少项,就像您对字符串所做的那样:

print(albums.count)

其次,您可以使用remove(at:)删除特定索引上的一个项目,或使用removeAll()删除所有内容,从数组中删除项目:

var characters = ["Lana", "Pam", "Ray", "Sterling"]
print(characters.count)

characters.remove(at: 2)
print(characters.count)

characters.removeAll()
print(characters.count)

删除字符后,将打印4,然后打印3,然后打印0。

第三,您可以使用contains()检查数组是否包含特定项目,如下所列:

let bondMovies = ["Casino Royale", "Spectre", "No Time To Die"]
print(bondMovies.contains("Frozen"))

第四,您可以使用sorted()对数组进行排序,如下:

let cities = ["London", "Tokyo", "Rome", "Budapest"]
print(cities.sorted())

这返回了一个新数组,其项目按升序排序,这意味着字符串按字母顺序排列,但数字按数字排列——原始数组保持不变。

最后,您可以通过调用reversed()来反转数组:

let presidents = ["Bush", "Obama", "Trump", "Biden"]
let reversedPresidents = presidents.reversed()
print(reversedPresidents)

提示:当你反转数组时,Swift非常聪明——它实际上并没有重新排列所有项目,而只是记住您希望项目被反转。因此,当你打印出超过reversedPresidents,不要惊讶地看到它不再只是一个简单的数组了!

数组在Swift中非常常见,随着你的进步,你将有很多机会更多地了解它们。更好的sorted()reversed()和许多其他数组功能也存在于字符串中——使用sorted()将字符串的字母按字母顺序排列,使“swift”变成“fistw”。

如何在字典中存储和查找数据

你已经看到了数组是如何存储具有特定顺序的数据的好方法,例如一周中的天数或城市的温度。当项目应该按照您添加它们的顺序存储时,或者当您可能有重复的项目时,数组是一个很好的选择,但通常通过其在数组中的位置访问数据可能会令人讨厌甚至危险。

例如,这是一个包含员工详细信息的数组:

var employee = ["Taylor Swift", "Singer", "Nashville"]

我告诉过你,数据是关于员工的,所以你也许能猜到各个部分做什么:

print("Name: \(employee[0])")
print("Job title: \(employee[1])")
print("Location: \(employee[2])")

但这有几个问题。首先,你无法真正确定employee[2]是他们的位置——也许那是他们的密码。其次,不能保证项目2甚至在那里,特别是因为我们把数组做成了一个变量。这种代码会造成严重的问题:

print("Name: \(employee[0])")
employee.remove(at: 1)
print("Job title: \(employee[1])")
print("Location: \(employee[2])")    

现在将纳什维尔打印为职位,这是错误的,当读取employee[2]时,会导致我们的代码崩溃,这很糟糕

Swift对这两个问题都有一个解决方案,称为字典。字典不像数组那样根据项目的位置来存储项目,而是让我们决定项目应该存储在哪里。

例如,我们可以重写我们之前的例子,以便更明确地说明每个项目是什么:

let employee2 = ["name": "Taylor Swift", "job": "Singer", "location": "Nashville"]

如果我们把它分成单独的行,你会更好地了解代码的作用:

let employee2 = [
    "name": "Taylor Swift",
    "job": "Singer", 
    "location": "Nashville"
]

正如你所看到的,我们现在非常清楚:名字是泰勒·斯威夫特,工作是辛格,地点是纳什维尔。Swift调用左侧的字符串——名称、工作和位置——字典的,右侧的字符串是

在从字典中读取数据时,您可以使用创建字典时使用的相同键:

print(employee2["name"])
print(employee2["job"])
print(employee2["location"])

如果您在游乐场中尝试,您将看到Xcode会发出各种警告,如“表达式隐式从'字符串中强制?”到“任何”。更糟糕的是,如果你查看游乐场的输出,你会看到它打印了Optional("Taylor Swift"),而不仅仅是Taylor Swift——什么给了?

好吧,想想这个:

print(employee2["password"])
print(employee2["status"])
print(employee2["manager"])

所有这些都是有效的Swift代码,但我们正试图读取没有附加值的字典键。当然,Swift可能会在这里崩溃,就像你读取一个不存在的数组索引时一样,它会崩溃,但这会使它非常难以使用——至少如果你有一个包含10项的数组,你知道读取0到9的索引是安全的。(“Indices”只是“index”的复数形式,以防您不确定。)

因此,Swift提供了一个替代方案:当您访问字典中的数据时,它会告诉我们“您可能会获得价值,但您可能什么也得不到。”Swift调用这些可选数据,因为数据的存在是可选的——它可能存在,也可能不存在。

当您编写代码时,Swift甚至会警告您,尽管以一种相当晦涩的方式——它会说“表达式隐式从'字符串'中强制?”到“任何”,但它实际上意味着“这些数据可能实际上不在那里——你确定要打印它吗?”

可选是一个相当复杂的问题,我们稍后会详细介绍,但现在我将向您展示一个更简单的方法:从字典中读取时,如果密钥不存在,您可以提供一个默认值来使用。

看起来是这样的:

print(employee2["name", default: "Unknown"])
print(employee2["job", default: "Unknown"])
print(employee2["location", default: "Unknown"])

所有示例都为键和值使用了字符串,但您可以为其中任何一个使用其他数据类型。例如,我们可以使用字符串来记录哪些学生已经毕业,并使用布尔值来记录他们的毕业状态:

let hasGraduated = [
    "Eric": false,
    "Maeve": true,
    "Otis": false,
]

或者我们可以跟踪奥运会举行的年份及其地点:

let olympics = [
    2012: "London",
    2016: "Rio de Janeiro",
    2021: "Tokyo"
]

print(olympics[2012, default: "Unknown"])

您还可以使用任何要存储的显式类型创建一个空字典,然后逐个设置密钥:

var heights = [String: Int]()
heights["Yao Ming"] = 229
heights["Shaquille O'Neal"] = 216
heights["LeBron James"] = 206

请注意,我们现在需要如何编写[String: Int],即一个字典,其键为字符串,其值为整数。

由于每个字典项必须存在于一个特定的密钥中,因此字典不允许存在重复的密钥。相反,如果您为已存在的键设置值,Swift将覆盖之前的任何值。

例如,如果你正在和朋友谈论超级英雄和超级恶棍,你可能会把它们存储在这样的字典里:

var archEnemies = [String: String]()
archEnemies["Batman"] = "The Joker"
archEnemies["Superman"] = "Lex Luthor"

如果你的朋友不同意小丑是蝙蝠侠的宿敌,你可以使用相同的键重写该值:

archEnemies["Batman"] = "Penguin"

如何使用集合进行快速数据查找

到目前为止,您已经了解了在Swift中收集数据的两种方法:数组和字典。还有第三种非常常见的数据分组方法,称为集合——它们类似于数组,只是您不能添加重复的项目,而且它们不会按特定顺序存储项目。

创建集合的工作原理与创建数组非常类似:告诉Swift它将存储什么样的数据,然后继续添加内容。然而,有两个重要的区别,它们最好用一些代码来证明。

首先,以下是你如何制作一组演员的名字:

let people = Set(["Denzel Washington", "Tom Cruise", "Nicolas Cage", "Samuel L Jackson"])

注意到它实际上是如何首先创建一个数组,然后将该数组放入集合中吗?这是有意的,这是从固定数据中创建集合的标准方法。记住,集合将自动删除任何重复的值,并且不会记住数组中使用的确切顺序。

如果您好奇该集是如何对数据进行排序的,请尝试将其打印出来:

print(people)

可能会在原始订单中看到名字,但您也可能得到一个完全不同的订单——这套只是不在乎其物品以什么顺序进来。

将项目添加到集合时,第二个重要区别是单独添加项目时可见的。这是代码:

var people = Set<String>()
people.insert("Denzel Washington")
people.insert("Tom Cruise")
people.insert("Nicolas Cage")
people.insert("Samuel L Jackson")

注意到我们如何使用insert()吗?当我们有一个字符串数组时,我们通过调用append()来添加项目,但这个名字在这里没有意义——我们没有在集合的末尾添加项目,因为集合将按照它想要的任何顺序存储项目。

现在,您可能会认为集合听起来只是简化的数组——毕竟,如果您不能重复,并且丢失了项目的顺序,为什么不直接使用数组呢?嗯,这两个限制实际上都变成了优势。

首先,不存储重复内容有时正是你想要的。我在前面的例子中选择演员是有原因的:美国演员协会要求其所有成员都有一个独特的艺名,以避免混淆,这意味着绝不允许重复。例如,演员迈克尔·基顿(《蜘蛛侠:归来》、《玩具总动员3》、《蝙蝠侠》等)实际上叫迈克尔·道格拉斯,但由于公会中已经有迈克尔·道格拉斯(《复仇者联盟》、《坠落》、《浪漫之石》等),他必须有一个独特的名字。

其次,集合不是按照您指定的确切顺序存储您的项目,而是以高度优化的顺序存储它们,这使得找到项目的速度非常快。区别不小:如果您有一个包含1000个电影名称的数组,并使用类似contains()的东西来检查它是否包含“黑暗骑士”,Swift需要浏览每个项目,直到找到一个匹配的项目——这可能意味着在返回false之前检查所有1000个电影名称,因为《黑暗骑士》不在数组中。

相比之下,在集合上调用contains()运行速度非常快,您很难有意义地衡量它。该死,即使你在集合中有一百万个项目,甚至有1000万个项目,它仍然会立即运行,而一个数组可能需要几分钟或更长时间才能完成同样的工作。

如何创建和使用枚举

枚举——枚举的缩写——是我们可以在代码中创建和使用的一组命名值。它们对Swift没有任何特殊意义,但它们更高效、更安全,因此您将在代码中经常使用它们。

为了演示这个问题,假设你想编写一些代码,让用户选择一周中的某一天。你可能会这样开始:

var selected = "Monday"

稍后在你的代码中,你改变它,就像这样:

selected = "Tuesday"

这可能在非常简单的程序中很好用,但看看这个代码:

selected = "January"

哎呦!你不小心输入了一个月而不是一天——你的代码会做什么?好吧,你可能很幸运,同事在审查你的代码时发现了错误,但这个怎么样:

selected = "Friday "

周五末尾有一个空格,在Swift眼中,带空格的“周五”与没有空格的“周五”不同。再说一遍,你的代码会做什么?

将字符串用于这种事情需要一些非常谨慎的编程,但它也相当低效——我们真的需要存储“星期五”的所有字母来跟踪一天吗?

这就是枚数的用处:它们让我们定义一个新的数据类型,其中包含一些特定的值。想想一个布尔值,它只能有真或假——你不能把它设置为“可能”或“可能”,因为它不在它理解的值范围内。枚举是一样的:我们可以提前列出它可以拥有的值范围,Swift将确保您使用它们永远不会出错。

因此,我们可以将工作日改写成像这样的新枚数:

enum Weekday {
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
}

这调用了新的枚数Weekday,并提供了五个案例来处理五个工作日。

现在,我们不使用字符串,而是使用枚枚。在你的游乐场里试试这个:

var day = Weekday.monday
day = Weekday.tuesday
day = Weekday.friday

通过该更改,您不能意外地使用带有额外空格的“星期五”,或者用月份名称代替——您必须始终选择枚举中列出的可能日期之一。当您输入Weekday.您甚至会看到Swift提供所有可能的选项,因为它知道您将选择其中一个案例。

Swift做了两件事,使枚枚枚枚更容易使用。首先,当你在枚记项中有很多案例时,你可以只写一次case写,然后用逗号分隔每个案例:

enum Weekday {
    case monday, tuesday, wednesday, thursday, friday
}

其次,请记住,一旦将值分配给变量或常量,其数据类型就会变得固定——您不能先将变量设置为字符串,然后再设置为整数。好吧,对于枚举,这意味着您可以在第一个任务后跳过枚举名称,如下:

var day = Weekday.monday
day = .tuesday
day = .friday

[本文翻译自hackingwithswift,点击链接阅读原文]

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区