Swift 的方式来使用数组

2020-08-24  本文已影响0人  小時間光
D2.jpg

GitHub地址

Array是在开发中最常用的数据类型之一,官方文档对Array的定义是:An ordered, random-access collection.。通常表示一个有序的集合,这里所说的有序并不是大小有序,而是指Array中元素的先后位置关系。

1、Swift的方式来操作数组

var testArray1: Array<Int> = Array<Int>()
var testArray2: [Int] = []
var testArray3 = [Int]()
var testArray4 = testArray3

在上面的代码块中,Array<Int>[Int]()没有本质区别,随便使用哪种方式都可以初始化一个Array。而最后一种直接使用了一个空的Array生成了一个新的Array对象。

var testInts = [Int](repeating: 6, count: 3) // [6, 6, 6]
var sixInts = testInts + testInts // [6, 6, 6, 6, 6, 6]
let testArray = ["one","two","three","four","five","six"]
if testArray.isEmpty == false {
    let mapArray = testArray.map({ (mapString) -> String in
         return mapString
    })
} else {
    print("数组为空")
}

在几乎所有的开发语言中都惯用索引(或下标)的方式访问数组数组中的元素,因为我们在使用索引访问数组的元素时没法保证索引的安全性,所以这种方式存在数组越界的风险,稍不注意就会导致整个程序直接Crash,从而给用户造成不好的体验。
当然Swift也支持这种方式,但苹果却不推荐开发者使用这种方式访问数组元素。

let testArray = ["one","two","three","four","five","six"]
let sevenStr = testArray[7]

如上面的代码,使用这种方式访问数组元素,如果直接访问不做处理会导致程序直接crash,在调试时,可以看到编译器直接报错:Thread 1: Fatal error: Index out of range, 所以在使用索引访问数组元素时应该慎重在慎重。那么在Swift中如何优雅的访问数组元素呢?事实上,Swift的设计者是不推荐开发者使用这种下标取值的方式。

使用type方法可以看到,这种方式返回的值为String类型,这说明在使用下标取值时需要确认,取到的值为String类型,而不是Optionals<String>类型,我们在使用这种方式取值时没有Optionals保护数组越界的情况,在使用下标取值的方法时我们就要承担crash的风险。

insert方法的第一个参数表示要插入的值,第二个参数表示要插入的位置索引,此处要注意的是:这个位置必须是一个合法的范围,即0...array1.endIndex,如果超出这个范围就会导致程序Crash

删除数组中的元素:

删除数组中的最后一个元素可以使用removeLast()方法,删除数组中的第一个元素可以使用removeFirst()方法,

var testArray = ["one","two","three","four","five","six","seven"]
testArray.removeLast() // ["one","two","three","four","five","six"]
testArray.removeFirst() // ["two","three","four","five","six"]
testArray.remove(at: 7) // Fatal error: Index out of range
var secondTestArray = [String]()
secondTestArray.removeFirst() // Fatal error: Can't remove first element from an empty collection
secondTestArray.removeLast() // Fatal error: Can't remove last element from an empty collection
secondTestArray.popLast() // nil 

当我们删除数组中的某个元素时,首先要确保数组不能为空,然后还要保证删除的索引在合法的范围内,然后在进行删除操作

查找元素在数组中的位置:

如我要查找元素5在数组中的位置,可以使用firstIndex,返回的是一个Optional<Int>

let testList = [1,2,3,4,5,6]
let oneIndex = testList.firstIndex { $0 == 5 }
print("ondexIndex===============\(oneIndex ?? 0)")
let testArray = ["one","two","three","four","five","six"]
testArray.forEach { (vaule) in
   print(vaule)
}

for inforEach遍历的效果是一样的,但是如果使用forEach方法遍历数组就不能通过breakcontinue来退出循环,如果要退出遍历就只能使用for in方法。

testArray.forEach { (testChar) in
if testChar == "three" {
    continue // 'continue' is only allowed inside a loop
}
}

事实上,Swift的设计者并不推荐开发者使用传统的C语言风格的for循环。Swift为开发者提供了更高级的方法,当我们需要循环一个数组时,可以根据自己的需求,使用Map 和 Filter来实现。

举例一:老师现在已经得到了学生的试卷成绩,再为每个学生加上平时成绩30分,这样可以得出学生的最终成绩,如果使用传统的C语言循环来实现:

let fractionArray: [Int] = [40,53,59,43,56,54,33,55,66,70,22,69]
var finallyList = [Int]()
for num in fractionArray {
  finallyList.append(num + 30)
}
// [70, 83, 89, 73, 86, 84, 63, 85, 96, 100, 52, 99]

再看看Swift为开发者提供的map方法:

let fractionArray: [Int] = [40,53,59,43,56,54,33,55,66,70,22,69]
let finallyArray: [Int] = fractionArray.map {
    return $0 + 30
}
// [70, 83, 89, 73, 86, 84, 63, 85, 96, 100, 52, 99]

map用于把数组中的所有元素按照指定规则操作变换并返回一个新的数组,这样比使用for循环更具表现力。

使用map来完成这类操作,大大提高了代码的可读性;
同时使用map直接返回了一个常量数组;

当然Swift的设计者只是对map封装了for循环的代码,其核心代码如下:

extension Array {
 public func myselfMap<T>(_ transform: (Element) -> T) -> [T] {
      var tmp: [T] = []
      tmp.reserveCapacity(count)
     
      for value in self {
          tmp.append(transform(value))
      }
      return tmp
   }
}  

flatMapcompactMapmap的升级版:

let lsArray: [Int] = testList.compactMap { (num) -> Int? in
 return num
} // [1, 2, 2, 3, 3, 4, 5, 6]

举例二: 老师在算出学生成绩后要查看那些人的成绩是优秀,此时我们可以使用filter

let excellentArray = finallyArray.filter {
   $0 >= 85
}
// [89, 86, 85, 96, 100, 99]

map在遍历数组的同时可以对每个参数做指定规则的操作,同时返回一个新的数组;
filter只按指定规则遍历数组,同时返回一个新的数组

filter是怎么实现的呢,我们可以根据map的实现方法来实现,其核心代码如下:

extension Array {
 func myfilter(_ predicate: (Element) -> Bool) -> [Element] {
     var tmp: [Element] = []
     for value in self where predicate(value) {
         tmp.append(value)
     }
     return tmp
 }
}

同样老师如果需要计算本次考试的最高分和最低分,只要数组中的元素实现了Equatable protocol协议,开发者不用对数组进行任何操作,可直接调用min 、 max即可:

finallyArray.min() // 52
finallyArray.max() // 100

接上面,如果老师要对本班成绩按从大到小或者从小到大的顺序排列,这样可以更直观的看到本班的成绩:

let mixArray = finallyArray.sorted() // [52, 63, 70, 73, 83, 84, 85, 86, 89, 96, 99, 100]
let maxArray = finallyArray.sorted(by: >) // [100, 99, 96, 89, 86, 85, 84, 83, 73, 70, 63, 52]
finallyArray.elementsEqual(mixArray, by: {
    $0 == $1
}) // false

如老师要计算出本次考试成绩,是否有满分的:

mixArray.starts(with: [100], by: {
    $0 == $1
}) // true

例如老师要计算本次考试的总成绩,然后计算出本次考试的平均成绩:

如果使用C语言风格,我们首先要对成绩做一个遍历,然后累加最终得到全班成绩总和:

var allNum = Int()
for num in finallyList {
   allNum = allNum + num
}
allNum / (finallyList.count)

Swift为开发者提供了更简单有效的方法:

finallyList.reduce(0, +) / (finallyList.count) // 81

例如老师要分别统计出及格和没及格的成绩,我们认为60分为及格:

let pass = mixArray.partition(by: {
    $0 > 60
})
let failedArray = mixArray[0 ..< pass] // 不及格的 [52]
let passArray = mixArray[pass ..< mixArray.endIndex] // 及格的 [63, 70, 73, 83, 84, 85, 86, 89, 96, 99, 100]

2、Array和NSArray

第一段代码中:复制testArray数组后并向testArray中添加元素,copyArray的内容并不会改变,因为在复制testArray数组时,并不是内容复制,而是内存地址复制,两个数组仍旧共用同一个内存地址,只有当修改了其中一个Array的内容时,才会生成一个新的内存。

第二段代码:复制mutArray数组,尽管copyArrayNSArray,此时我希望copyArray的值是不会改变的,但实际上当我修改了mutArray的值后copyArray的值也随之改变了。由于这个赋值执行的是引用拷贝,这两个数组指向的是同一个内存地址,所以当我们修改mutArray的内容时,copyArray也就间接受到了影响。

本文主要介绍了Swift中最常用的集合Array的一些基本用法,和Objective-CNSArray的区别。

友情链接:

Apple developer Array

泊学:Swift 3 Collections

极客时间:集合类

上一篇 下一篇

猜你喜欢

热点阅读