精华内容
下载资源
问答
  • 集合不能通过下标访问 >>> X[0] 集合是没有顺序的,所以不能通过下标获取集合的某个元素 Traceback (most recent ca...
    • 集合不能通过下标访问
    1. >>> X[0]    集合是没有顺序的,所以不能通过下标获取集合的某个元素
            
      Traceback (most recent call last):
        File "<pyshell#300>", line 1, in <module>
          X[0]
      TypeError: 'set' object does not support indexing

    转载于:https://my.oschina.net/u/3718603/blog/2441344

    展开全文
  • 在大多数编程语言中,字符串是可以直接通过下标访问的,但是在使用 go 语言的时候,直接使用下标访问有时候会出现一些乱码。 在解决这个问题之前,要先了解一个东西--数组:数组是用于存储多个相同类型数据的集合。...

    61cd614cf5e2b8fc807f2634f14571ec.png

    在大多数编程语言中,字符串是可以直接通过下标访问的,但是在使用 go 语言的时候,直接使用下标访问有时候会出现一些乱码。

    在解决这个问题之前,要先了解一个东西--数组:数组是用于存储多个相同类型数据的集合。并且数组在申请内存的时候,是一次申请一块连续的内存。比如我们创建一个数组,里面存了这几个元素。

    6d5500eb83fd22ba83805c72b6bd261c.png

    由于内存是连续的,元素的类型也是相同的,所以每个元素占用的内存空间也是固定的,比如 java 中 char 类型占用两个字节。数组的内存空间是平等划分的,这样就可以解释为什么可以靠下标访问了。

    在可以按照下标访问的语言中,字符串都是按照字符编码的。也就是说,你将字符串 “abcd” 赋给变量 a,本质上是创建了一个字符数组用来存放字符串。但是在 go 语言里不一样,go 语言的字符型是按照字节编码的,什么意思呢? 26 个英文字母,每个英文字母占一个字节,在 go 语言的 string 里面就占用一个字节。中文日文韩文就不一样了, go 语言内建只支持 utf8 编码,在 utf8 里面,有一部分汉字占用 3 个字节,一部分汉字占用 4 个字节。比如 "巧" 这个字,打印一下它的长度,发现这个 string 占用 3 个字节,加上 "a" 之后占用 4 个字节,应该能理解按字节编码的意思了。

    331dc10c57e6bd95f9f6b8aa0646dff7.png

    为什么要 go 要选择按照字节来编码呢,这其实是为了节省空间。想象一下,在UTF-8编码中,中文有些要三个字节,有一些要占用四个字节,而英文字母只需要占用一个字节。一个中文算一个字符,一个英文字母也算一个字符,但是占用的内存相差很大,假设有一个超长字符串,里面有英文字符远多于中文字符,如果按字符来存储,每个字符要分配四个字节。因为低于四个字节,有可能有些中文就不能正常存储了,在这种情况下,每存储一个英文字母,就要浪费三个字节的内存空间。

    底层实现和其它语言就不一样,不同类型的字符占用的内存空间都不同,当然也就没有办法按照下标访问了,不信可以试试。

    4e855a7f92608f73743ef9574ceb0942.png

    a[0] 是 97,等于字母 a 的 ascii 码,a1 是 229,显然不会是汉字 "巧" 的 utf8,事实它是 utf8 编码的第一字节的值。

    78317696376f9e52ea6b51b4147aba72.png

    打完收工,到这里弄清楚了 go 中 string 不能按照下标访问的原理了

    展开全文
  • element-ui 的时间范围选择控件提交的值是一个时间数组 后端接收到在mybatis中查询的时候写 #{datetime[0],javaType=java.util.Date, jdbcType=DATE} #{datetime[1],javaType=java.util.Date, jdbcType=DATE} ...

    element-ui 的时间范围选择控件提交的值是一个时间数组
    后端接收到在mybatis中查询的时候写
    #{datetime[0],javaType=java.util.Date, jdbcType=DATE}
    #{datetime[1],javaType=java.util.Date, jdbcType=DATE}
    可以将时间转换为date,即根据日期查询

    展开全文
  • 但不是所有的下标都可以随机访问,可以随机访问下标都是整数。在Swift中,一个类型想要作为集合下标,至少需要实现ForwardIndexType协议。这个协议很简单,只有两部分构成:一个successor()方法以及实现...

    到目前为止,我们在数组和自己实现的队列中,一直用整数作为集合的下标.

    但不是所有的下标都可以随机访问,可以随机访问的下标也不都是整数。在Swift中,一个类型想要作为集合的下标,至少需要实现ForwardIndexType协议。这个协议很简单,只有两部分构成:一个successor()方法以及实现Equatable协议,也就是说这个类型需要一个==运算符。

    为了解释的更加清楚,我们来实现一个最基本的,只能前向访问的集合:单链表。在此之前,我们先展示一种通过间接枚举来实现数据结构的方法。

    链表的节点有两部分内容:一个是数据,另一个是下一个节点的引用(如果是链表的尾部,就没有引用了)。我们可以这样定义:

    enum List<Element> {
    //链表结尾
    case End
    indirect case Node(Element, next: List<Element>)
    }
    复制代码

    indirect关键字表示编译器要把这个值当做一个引用。Swift的枚举是值类型,这意味着枚举直接直接持有枚举值,而不是持有枚举值的引用。这种做法有很多优点,我们在后面的章节中会详细讨论。但这同时也表示枚举无法持有对自己的引用。indirect允许某个枚举值作为一个引用被持有,因此也就可以持有指向自己的引用了。

    要想向列表的表头添加一个节点,你可以创建一个新节点,并把这个节点的next指向当前节点。为了简化这个过程,我们可以创建一个函数:

    extension List {
    //返回一个以x开头的链表
    func cons(x: Element) -> List {
    return .Node(x, next:self)
    }
    }
    
    //创建一个有三个元素的链表,(3,2,1)
    let l = List<Int>.End.cons(1).cons(2).cons(3)
    复制代码

    我们把这个向链表头部添加元素的方法叫做cons是因为在LISP语言中它就是这么叫的。

    这种链表具有一个有趣的特性:它是永久的。节点一旦创建就不能改变了,把另一个新元素添加到现有链表的头部不会复制链表,只会给你一个新的节点,这个节点的next指向原来链表的头部。

    这意味着两个链表变量可以共享同一段尾部。

    这是,链表的不可变性就很关键了。如果链表可以被修改(比如移除最后一个节点或修改某一个节点中存的值),那多个变量之间的共享就会出问题。对其中一个的修改会影响到另一个。

    这个链表也可以理解成一个栈。cons方法类似于push,获取下一个节点相当于pop。我们之前还提到过数组也是栈,所以我们来定义一个通用的栈协议:

    protocol StackType {
    /// 栈中存放的数据的类型别名
    typealias Element
    
    /// 把'x'压入栈顶
    /// 平均时间复杂度O(1)
    mutating func push(x: Element)
    
    /// 将栈顶元素弹栈
    /// 如果栈为空返回nil
    /// 平均时间复杂度O(1)
    mutating func pop() -> Element?
    }
    复制代码

    这一次的注释要详细一些,包括了函数调用性能的保证。

    可以让数组遵守StackType协议:

    extension Array: StackType {
    mutating func push(x: Element) {
    self.append(x)
    }
    
    mutating func pop() -> Element? {
    guard !self.isEmpty else { return nil }
    return self.removeLast()
    }
    }
    复制代码

    也可以让链表实现StackType协议:

    extension List: StackType {
    mutating func push(x: Element) {
    self = .Node(x, next: self)
    }
    
    mutating func pop() -> Element? {
    switch self {
    case .End: return nil
    case let .Node(x, next: xs):
    self = xs
    return x
    }
    }
    }
    复制代码

    但我们之前不是说了链表是不可变的么,为什么它还会有被标记为mutating的方法呢?

    因为这些变异方法并不是改变链表,而是改变变量所指向的链表中的部分[1]

    var stack = List<Int>.End.cons(1).cons(2).cons(3)
    var a = stack
    var b = stack
    
    a.pop()  // 3
    a.pop()  // 2
    a.pop()  // 1
    
    stack.pop() // 3
    stack.push(4)
    
    b.pop()  // 3
    b.pop()  // 2
    b.pop()  // 1
    
    stack.pop()  // 4
    stack.pop()  // 2
    stack.pop()  // 1
    复制代码

    这段代码展示了变量和值的区别。链表的节点是值,它们是不能改变的。一个存储了数据3,同时还有一个next引用的节点,不可能变成其他的值。它永远都是当前这个状态,它所包含的数据也不能变。

    但是对于变量a来说,它可以改变它所持有的值。它可以持有任何一个节点的间接引用,或者直接只有末端节点。但是对a的改变不会影响到节点本身,只会改变a到底引用的是哪一个节点。

    这就是编结构体的变异方法的定义:他们有一个隐式的inout参数self,所以可以改变self持有的值。链表本身不能被改变,但可以改变变量当前所指向数组的哪一部分。在函数章节我们会讨论更多关于inout关键字的细节。

    从这个意义上讲,通过indirect关键字,变量变成了链表的迭代器。

    你当然可以把变量声明成let,这样变量就变成了常量(也就是说变量的值一旦确定,就不能再修改了)。但let修饰的是变量而非值,虽然值也是固定不可修改的,但这是由队列定义决定的。

    以上是整个链表的模型,实际上节点被放在内存中,它们占据一定空间,彼此指向其他节点。当它们不再需要时,Swift用自动引用计数(ARC)来管理,释放不再使用的节点占用的内存。在后面的章节中我们还会详细讨论ARC

    ###链表实现SequenceType协议

    刚刚我们提到链表变量其实是一种迭代器,所以它可以实现SequenceType协议:

    extension List: SequenceType {
    func generate() -> AnyGenerator<Element> {
    // 用一个变量截获self的状态,通过这个变量迭代
    var current = self
    return anyGenerator {
    //调用next方法时会调用到pop,如果链表为空返回nil
    current.pop()
    }
    }
    }
    复制代码

    为了让链表使用更简单,你可以让它也实现ArrayLiteralConvertible协议:

    extension List: ArrayLiteralConvertible {
    init(arrayLiteral elements: Element...) {
    self = elements.reverse().reduce(.End, combine: { $0.cons($1) })
    }
    }
    复制代码

    现在就可以使用for .... in语法了:

    let l: List = ["1", "2", "3"]
    for x in l {
    print("\(x) ",terminator: "")
    }
    复制代码

    同时,由于实现了ArrayLiteralConvertible协议,我们还可以用很多标准库中定义的库函数:

    l.joinWithSeparator(",")            // "1,2,3,"
    l.contains("2")                     // true
    l.flatMap { Int($0) }               // [1,2,3]
    l.elementsEqual(["1", "2", "3"])    // true
    复制代码

    实现CollectionType

    接下来让链表类实现CollectionType协议。这样我们可以访问链表变量的.first属性,并在不用pop的情况下获取第一个元素(相当于peek方法)

    更重要的是,实现CollectionType协议可以保证将序列多次传递是安全的。官方文档里的描述是:

    实现SequenceType协议的类不关心是否可以反复迭代,如果要保证它在迭代后不会被破坏性[2]”消耗“,就让它实现CollectionType协议。

    这解释了为什么只有集合才有first属性——提供计算属性的集合,应该是非破坏性迭代的。至于破坏性迭代的序列,可以参考readLine()函数。对于这样的序列,读取到后面就不能回头,自然也就无法提供first属性了。

    let standardIn = AnySequence {
    return anyGenerator {
    readLine()
    }
    }
    复制代码

    现在你可以用这个序列和SequenceType的协议拓展来写一个行数计算版本的Unix cat了:

    let numberedStdIn = standardIn.enumerate()
    for (i, line) in numberedStdIn {
    print("\(i+1): \(line)")
    }
    复制代码

    enumerate把一个序列包装在新的序列中,并提供一个递增的下标。和这里对readLine()函数的包装类似,enumerate得到的序列中的元素也是懒创建的。只有在你通过生成器,在新序列里遍历时,才会读取原序列中的元素。所以如果你运行上面这段代码,你输入的内容在你按回车时被打印出来,而不是等到按下'control+D'结束输入。

    enumerate方法的实现可能如下:

    extension SequenceType {
    func enumrate() -> AnySequence<(Int, Generator.Element)> {
    return AnySequence { _ -> AnyGenerator<(Int, Generator.Element)> in
    //创建一个新的计数器和生成器开始枚举
    var i = 0
    var g = self.generate()
    
    //在闭包中截取这些变量并在一个新的生成器中返回他们
    return anyGenerator{
    // 当原序列读到末尾时返回nil
    guard let next = g.next() else { return nil }
    return (i++, next)
    }
    }
    }
    }
    复制代码

    但尽管如此,每一次enumratenumberedStdIn序列中读取一行输入,这一行就无法再次被获取了,也就是说你无法遍历原序列两次并获得相同的值。

    有些序列我们可以多次遍历,其中一些序列不必是集合。比如stride方法返回的StrideToStrideThrough类型就不是集合。虽然在遍历浮点数时,它们有时候看上去像是集合,不过它们确实只是序列。但他们毫无疑问是可重用的。如果你想要自己拓展SequenceType协议,你不必考虑序列的遍历是否是破坏性的。但如果你要调用SequenceType的方法,那就要搞清楚这个问题了。

    我们的链表可以反复遍历,所以如果能实现CollectionType协议是再好不过了。但在此之前,先把节点从链表和下标类型中分离开来。

    一种很自然的想法是直接让枚举实现CollectionTypeForwardIndexType协议,不过这样会可能会导致一些问题。比如这两个协议对==运算符的要求完全不同[3]

    • ForwardIndexType协议需要知道同一个链表的两个下标是否指的是同一位置。它不要求元素本身实现Equatable协议
    • CollectionType协议则需要比较两个不同的链表,看它们是否包含相同的元素,这就需要元素实现Equatable协议

    我们可以把集合和下标用不同的类型表示,这样就可以分别为他们实现==运算符了。同时,因为这两者都不是节点的枚举,我们可以让节点的实现细节变成私有的,对集合的使用者隐藏。

    我们还需要做一些调整才能实现下标的==运算符。正如我们之前讨论过的,节点是值类型,因此它没有自己的标识,所以如何区分两个变量是否指向同一个节点呢?为了解决这个问题,我们需要改变一下cons方法使它能用一个递增的数标记每个节点。这样一来,同一个链表中的两个tag相同的节点一定是相同的了:

    private enum ListNode<Element> {
    //链表结尾
    case End
    indirect case Node(Element, tag: Int, next: ListNode<Element>)
    
    //用于获取tag的计算属性,.End的tag默认为0
    var tag: Int {
    switch self {
    case .End: return 0
    case let .Node(_, tag: n, next: _): return n
    }
    }
    
    func cons(x: Element) -> ListNode<Element> {
    return .Node(x, tag: tag+1, next: self)
    }
    }
    复制代码

    接下来是我们在链表中要用到的下标:

    public struct ListIndex<Element> {
    private let node: ListNode<Element>
    }
    复制代码

    ListIndex要做的就是把ListNode封装起来,它没有多余的信息,所以ListNodeListIndex占用内存的大小是一样的:

    assert(sizeof(ListNode<Int>) == sizeof(ListIndex<Int>))
    复制代码

    ListIndex隐藏了ListNode的具体实现。它自己也可以实现ForwardIndex协议,这是任何一个想要可索引的类型都必须遵守的协议。

    这个无成本的抽象允许我们构建一些有用的类型而不必考虑影响性能。它可以帮助我们更好的向编译器解释我们的真正目的。

    还需要注意一点,ListIndex是一个拥有私有属性(node)的公有(public)结构体。这意味着它的初始化不是公有的,因为它的“默认”构造器无法直接被用户使用。所以你可以从链表中获得ListIndex,却不能自己创建一个。

    ForwardIndexType协议只有两个要求。首先,实现它的类型必须有一个successor()方法返回下一个下标:

    extension ListIndex: ForwardIndexType {
    public func successor() -> ListIndex<Element> {
    switch node {
    case .End: fatalError("cannot increment endIndex")
    case let .Node(_, _, next: next): return ListIndex(node: next)
    }
    }
    }
    复制代码

    以及第二点:实现Equatable协议,这样才能判断两个下标是不是相等的。对于链表节点,我们只要判断它们的tag是否相同:

    public func == <T>(lhs: ListIndex<T>, rhs: ListIndex<T>) -> Bool {
    return lhs.node.tag == rhs.node.tag
    }
    复制代码

    现在ListIndex结构体就成功实现ForwardIndexType协议了,他现在可以被实现了CollectionType的链表使用了:

    public struct List<Element>: CollectionType {
    //下标类型可以被自动推导,不过显示地标出有助于理解代码
    public typealias Index = ListIndex<Element>
    
    public var startIndex: Index
    public var endIndex: Index
    
    public subscript(idx: Index) -> Element {
    switch idx.node {
    case .End: fatalError("Sbuscript out of range")
    case let .Node(x, tag: _, next: _): return x
    }
    }
    }
    复制代码

    为了让链表更容易创建,我们实现一下ArrayLiteralConvertible协议:

    extension List: ArrayLiteralConvertible {
    public init(arrayLiteral elements: Element...) {
    startIndex = ListIndex(node: elements.reverse().reduce(.End, combine: { $0.cons($1) }))
    endIndex = ListIndex(node: .End)
    }
    }
    复制代码

    现在可以用字面量创建链表了:

    let l: List = ["one", "two", "three"]
    l.first
    l.indexOf("two")
    复制代码

    还有一个福利:因为tag其实就是.end节点前所有节点的数量,所以链表有了一个可以在常量级时间访问的属性count,这样就不需要用O(n)的时间通过下标遍历链表了。

    extension List {
    public var count: Int {
    return startIndex.node.tag - endIndex.node.tag
    }
    }
    复制代码

    这里做一个减法(虽然endIndex的tag目前来看一定是0)是为了在使用链表切片时也能正确计算长度。

    最后,由于链表和下标现在是不同的类型,所以就可以单独实现链表的==运算符了:

    public func == <T: Equatable>(lhs: List<T>, rhs: List<T>) -> Bool {
    return lhs.elementsEqual(rhs)
    }
    复制代码

    实现自定义切片

    和默认的下标生成器类似,集合还提供了切片的默认实现,语法是[Range<Index>],这就是dropFirst方法的实现原理:

    let firstDropped = l[l.stratIndex.successor()..<l.endIndex]
    复制代码

    在默认情况下,firstDropped的类型不是链表而是链表切片Slice<List<String>>。切片是基于集合的轻量级包装,它的实现大约是这样:

    struct Slice<Base: CollectionType>: CollectionType {
    let collection: Base
    let bounds: Range<Base.Index>
    
    var startIndex: Base.Index { return bounds.startIndex }
    var endIndex: Base.Index { return bounds.endIndex }
    
    subscript(idx: Base.Index) -> Base.Generator.Element {
    return collection[idx]
    }
    
    typealias SubSequence = Slice<Base>
    subscript(bounds: Range<Base.Index>) -> SubSequence {
    return Slice(collection: collection, bounds: bounds)
    }
    }
    复制代码

    默认的Slice封装了原来的集合,还提供了一个下标子区域。所以这样的切片的大小是同样的数组的两倍[4]

    ///链表的大小是两个节点的大小(开始节点和结束节点)
    sizeofValue(l)  			// 结果为16
    
    ///链表切片的大小由切片内的链表大小,和子范围的大小组成。
    ///子范围指的是两个下标之间的范围,在链表看来,这是一些节点
    sizeofValue(l.dropFirst())  // 结果为32
    复制代码

    这是因为通过持有不同的开始和结束下标(startIndexendIndex),链表可以把自己当作子序列返回。我们可以让链表实现自定义的操作:

    extension List {
    private init(subRange: Range<Index>) {
    startIndex = subRange.startIndex
    endIndex = subRange.endIndex
    }
    
    public subscript(subRange: Range<Index>) -> List<Element> {
    return List(subRange: subRange)
    }
    }
    复制代码

    这样,链表切片它们自己也是链表类型的了:

    sizeofValue(l.dropFirst())  // 结果为32
    复制代码

    这会给链表的实现带来另一个好处。我们知道Swift中有很多切片,比如数组切片、字符串切片等。而切片和原集合共享同一块存储空间,这回导致一个很讨厌的副作用:即使原来的集合已经超出其作用域 ,切片会一直持有这块存储空间。假设你的某个数组占用1G内存,然后产生了一个很小的切片,在这个切片被销毁之前这1G的内存空间都不会被释放。

    对于链表来说,这还不算最坏的。我们之前已经看到,节点通过ARC管理:当只有切片还引用这一块内存时,dropFirst部分因为没有被引用,就会立刻被释放。但尾部的节点不会释放,因为切片的最后一个下标还指向在链表中的后面一个节点。

    切片的默认实现需要我们注意另一件事:即使是使用整数作为下标,也不要假设集合的下标从0开始。

    比如的队列用的是默认的切片实现,所以如果你创建一个队列然后切割它,就会得到一个Slice<Queue<Element>>类型的切片:

    let q: Queue = ["a", "b", "c", "d", "e"]
    let s = q[2..<5]
    复制代码

    现在这个切片的开始下标是子范围的开始下标,切片的结束下标是子范围的结束下标:

    q.startIndex   // 0
    q.endIndex	   // 5
    s.startIndex   // 2
    s.endIndex	   // 4
    复制代码

    这就是为什么我们倾向于使用for x in collectionfor index in collection.indices而不是用C语言风格的代码。当你遍历一个集合时,总是记得把代码写成var i = collection.startIndex而不是var i = 0

    前向下标

    我们选择实现一个简单的链表是因为读者很熟悉它只能前向遍历的特点。你不能直接跳到链表的中间,也不能从链表的尾部向前遍历。你只能老老实实的一个一个向后遍历。

    出于这个原因,我们的链表集合,有一个first属性,却没有last属性。如果你确实想要读取最后一个节点,你需要一路遍历下去直到最后一个节点。由于这是一个O(n)复杂度的算法,所以提供last属性可能误导别人用它,但实际上这样的性能非常低。

    提供一个快捷访问的属性需要保证这个属性可以在大约常量级时间中被获取,除非这样的操作显然不可能用常量级时间完成。

    这是一个有点含糊的定义,“必须在常量级时间中完成”这个定义会更好,因为可以排除类似于"hello".uppercaseString这样明显不是常量级时间但也是合理的计算属性。

    函数需要另外考虑。我们的链表类型没有reverse操作:

    let reversed = l.reverse()
    复制代码

    上面代码中被调用的reverse是标准库对SequenceType类型的拓展。任何SequenceType类型都可以用这个函数:

    extension SequenceType {
    // 返回一个把自己含有元素逆序排列的序列
    func reverse() -> [Self.Generator.Element]
    }
    复制代码

    这个函数返回的是一个数组,但我们希望它能返回一个链表。所以我们可以拓展一下链表的reverse()函数:

    extension List {
    public func reverse() -> List<Element> {
    let reversedList: ListNode<Element> = self.reduce(.End, combine: { $0.cons($1) })
    
    return List(startIndex: ListIndex(node: reversedList), endIndex: ListIndex(node: .End))
    }
    }
    复制代码

    现在,当你调用一个链表的reverse方法时,得到的是另一个链表。这是因为Swift的函数重载机制,它会选择执行最具体的那个函数实现(区别于范型函数)。因此当我们调用链表的reverse方法时,调用的是我们自己的方法实现而不是SequenceType协议提供的默认实现。

    有时候我们需要的确实就是数组,这种情况下用SequenceType协议提供的默认实现会更方便,因为用我们自己的实现需要把链表再转成数组,这样就多了一步。这时候,我们可以指定返回值的类型,就可以强制Swift执行其默认的reverse方法了:

    let reversedArray: [String] = l.reverse()
    复制代码

    如果这个结果要放到函数中做参数使用,我们可以使用as关键字。比如下面这行代码表示检查我们自定义的reverse方法是否和默认的reverse方法结果一致:

    l.reverse().elementsEqual(l.reverse as [String])
    复制代码

    提示: 做这样的测试需要确保函数重载确实发生了,否则你其实调用了两个一样的方法,测试当然会通过

    你可以用is关键字来检查返回结果是否是List类型,但是编译器会告诉你这样做是没有意义的(因为事实上重载是发生了的,所以判断结果总是为true)。为了避免这个警告,你可以这么写:

    l.reverse() as Any is List<Int>
    复制代码

    双向下标

    BidirectionalIndexType协议为下标添加了一个非常关键的方法:predecessor()。这使得我们的链表可以有一个和first属性对应的last属性:

    extension CollectionType where Index: BidirectionalIndexType {
    var last: Generator.Element? {
    guard !isEmpty else { return nil }
    return self[endIndex.predecessor()]
    }
    }
    复制代码

    Swift标准库中,String.CharacterView就有双向下标,我们在字符串章节会对和Unicode相关的知识做进一步介绍。字符串集合不能被随机访问,但可以从最后一个字符向前,一个一个地遍历。

    String.CharacterView还有一个非常高效的reverse操作,它不会立刻反转集合,而是返回一个懒视图:

    extension CollectionType where Index: BidirectionalIndexType {
    // 返回一个懒加载的CollectionType,包含了反向排列的self元素
    func reverse() -> ReverseCollection<Self>
    }
    复制代码

    实际上并没有reverse操作发生。ReverseCollection只是持有了原来的结合并且反悔了一个successorpredecessor都相反的下标。我们只要对原来的集合做很少的封装,改变遍历方法就可以做到这一点:

    struct ReverseIndex<Base: BidirectionalIndexType>: BidirectionalIndexType {
    let idx: Base
    func successor() -> ReverseIndex<Base> {
    return ReverseIndex(idx: idx.predecessor())
    }
    
    func predecessor() -> ReverseIndex<Base> {
    return ReverseIndex(idx: idx.successor())
    }
    }
    
    func ==<T>(lhs: ReverseIndex<T>, rhs: ReverseIndex<T>) -> Bool {
    return lhs.idx == rhs.idx
    }
    
    struct ReverseCollection<Base: CollectionType where Base.Index: BidirectionalIndexType> :CollectionType {
    let base: Base
    
    var startIndex: ReverseIndex<Base.Index> { return ReverseIndex(idx: base.endIndex) }
    var endIndex: ReverseIndex<Base.Index> { return ReverseIndex(idx: base.startIndex) }
    
    subscript(idx: ReverseIndex<Base.Index>) -> Base.Generator.Element {
    return base[idx.idx.predecessor()]
    }
    }
    复制代码

    在这段代码中,变量值的复制非常重要。在创建ReverseCollection时,我们把原集合“复制”到base变量中。这其实是写实复制,但对于数组(或者其他不可变的持久化数据结构如链表,队列等)来说非常高效。即使原集合的元素被改变,也不会影响到base中拷贝的元素。这意味着ReverseCollection和其他用reverse方法返回的数组一样,具有可观察的行为。

    随机下标[5]

    #译者注

    [1]:书上的解释,还是不太清楚,其实用文字解释有点困难。比如说链表本身是[4,3,2,1],一个变量刚开始指向的节点是4,这时候它其实包括了[4,3,2,1]这部分。后来执行pop操作之后,变量指向了节点3,这时候它包括了[3,2,1]部分。也就是变量自身改变了,但链表本身没有改变。

    [2]:所谓的非破坏性,是指能够多次遍历。之前我们见过某些数据结构,从头到尾遍历一次就不能再遍历了,比如一个普通的生成器。

    [3]:对swift不熟的童鞋需要注意,这里强调==运算符是因为swift中对运算符重载的要求比较严。它不能定义在某个类的内部,而是写成全局函数,通过参数类型来确定到底重载哪个类型的的运算符。所以如果把下标和链表都放在枚举中,就只能重载枚举类型的==运算符了。也就是说CollectionTypeForwardIndexType协议所需要的==必然有一个不能满足。

    [4]:切片定义中的两个变量是计算属性,不占用空间,因为他们不存储实际的东西。collection变量就是集合,bounds属性是一个子范围,观察一下定义发现它也是一个集合。所以最终切片的大小是原来链表的两倍。

    [5]:这一节目前只有代码没有解释,等正式版出来之后再翻译吧

    展开全文
  • 是字符的集合使用单引号、双引号、三引号引住的字符序列字符串是可变对象如图,首先 a的值为ab,内存地址为18016640 然后加上了一个c 值变了 但是内存地址也变了 只要地址变了 所以这个对象就是变了字符串元素访问...
  • Swift3集合访问

    2017-05-15 15:20:41
    Set是一个无序的集合,我们不能像数组那样通过下标集合元素进行访问。我们可以通过for-in循环来遍历一个集合。  1 | for str in setStr {  2 | print("\(str)")  3 | } 我们可以通过...
  • Untitled Document.md 在 Swift 中,我们可以使用下标轻松的访问数组中的元素。比如这样: var numbers:Array = [1,2...还可以以字典的模式来进行下标访问,比如这样: var props:Dictionary<String,String&...
  • 获取获取List集合中,指定下标之间的数据。 代码实现 测试:在list中添加数据0-10;获取下标为2和8之间的数据 @Test public void test() { List<Integer> list = new ArrayList<>(); for (int i = 0; ...
  • swift下标

    2018-12-15 10:47:23
    2.12 下标 本页包含内容: 下标语法 下标用法 ...举例来说,用下标访问一个Array实例中的元素可以写作someArray[index],访问Dictionary实例中的元素可以写作someDictionary[key]。 一个类型可以定义...
  • foreach获取访问元素的下标

    千次阅读 2016-02-26 16:20:00
    今天在foreach循环的时候,要同时根据访问下标获取另一个list对象数据,之前想的方法是加一个变量i,然后每次i++,但是感觉这样不是很好!,后来发现这样也可以!举个简单的例子! foreach(var item in list) ...
  • java集合类之随机访问和顺序访问

    千次阅读 2017-12-04 14:32:32
    随机访问一般是通过index下标访问,行为类似数组的访问。而顺序访问类似于链表的访问,通常为迭代器遍历。 以List接口及其实例为例。ArrayList是典型的随机访问型,而LinkedList则是顺序访问型。List接口既定义了...
  • 数组与集合的应用之数组操作(数组的下标界限)
  • 最近在做的项目中有绘制图表的功能,需要实时更新显示数据,本想队列来存储数据,通过入队出队来更新数据,但普通队列只支持访问队头元素,而绘制图表需要遍历队列,于是就有了自己实现一个可随机访问队列的想法。...
  • Swift 下标脚本

    2021-01-06 02:48:06
    下标脚本 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,需要再调用实例的特定的赋值和访问方法。 举例来说,用下标脚本访问一个数组...
  • Java集合容器面试题(2020最新版)

    万次阅读 多人点赞 2020-03-01 11:08:34
    文章目录集合容器概述什么是集合集合的特点集合和数组的区别使用集合框架的好处常用的集合类有哪些?List,Set,Map三者的区别?List、Set、Map 是否继承自 Collection ...怎么确保一个集合不能被修改?Collection...
  • 数组中的一个特别要素是通过下标访问它。数组提供了一种将有联系的信息分组的便利方法。 注意:如果你熟悉C/C++,请注意, Java数组的工作原理与它们不同。 1、数组不是集合,它只能保存同种类型的多个原始类型...
  • ​ ​ ​ <div class='danxuan' th:if="${dan.size()>0}"> <div style="font-size: 18px;"><img th:src="@{/assets/img/classroom/ti.png}" alt="" st...
  • Swift-下标

    2018-07-27 04:29:18
    举例来说,用下标访问一个Array实例中的元素可以写作someArray[index],访问Dictionary实例中的元素可以写作someDictionary[key]。 一个类型可以定义多个下标,通过不同索引类型进行下载。下标限于一维,你可以定....
  • swift 下标

    2017-03-13 14:01:26
    与定义实例方法类似,定义下标使用subscript关键字,指定一个或多个输入参数和返回类型:与实例方法不同的是,下标可以设定为读写或只读。这种行为由getter和setter实现,有点类似计算属性。 struct TimesTable { ...
  • swift 下标 subscript

    2020-08-26 23:24:36
    它可以作为访问集合,列表或序列成员元素的快捷方式。你可以使用下标通过索引值来设置或检索值而需要为设置和检索分别使用实例方法。 你可以作为一个类型定义多个下标,并且下标会基于传入的索引值的类型选择合适...
  • 平时经常会有一些对集合的增删场景,那么普通for循环、foreach、迭代器它们会有一些什么区别呢?fail-fast机制又是怎么体现的呢?这篇文章从源码和应用的角度进行了全面的分析。
  • Swift学习:下标

    2017-11-30 06:55:00
    举例来说,用下标访问一个 Array 实例中的元素可以写作 someArray[index],访问 Dictionary 实例中的元素可以写作 someDictionary[key]。  一个类型可以定义多个下标,通过不同索引类型进行重载。下标限于一维.....
  • 集合(set)的特点 无序,不能用索引访问 元素不重复,会自动去重 符号为 { }
  • 12.下标

    2016-07-18 13:11:00
    下标可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而需要再调用对应的存取方法。  一个类型可以定义多个下标,通过不同索引类型进行重载。下标不...
  • 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问集合(collection),列表(list)或序列(sequence的快捷方式,使用下标脚本的索引设置和获取值,需要再调用实例的...
  • 今天我的同学问了关于访问Set集合的元素问题, 由于Set是无序的集合,所以不能根据下标去直接访问,需要进行一些处理, 主要方法是将Set集合转换成数组    下面是代码   package org.aiy.test; import ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 103,579
精华内容 41,431
关键字:

不能用下标访问的集合