Scala之List

Scala之List

数据结构

scala.collection.immutable.List是有序集合的不可以变的链表,它是递归结构

它有两个实现的case class 'scala.Nil’和’scala.::,它们实现的抽象的成员 ‘isEmpty’,‘head’和’tail’
Nil代表一个空的List, :: 操作是在list的前面拼接

    val fruit = "apples" :: ("oranges"::("paers" :: Nil))
    val nums = 1 :: (2::(3::(4::Nil)))
    val nums2 = 1 :: 2::3::4::Nil
    val diag3 = (1::(0::(0::Nil))) ::
                (0::(1::(0::Nil))) ::
                (0::(0::(1::Nil))) :: Nil
    println("fruit:"+fruit)
    println("nums:"+nums)
    println("num2:"+nums2)
    println("diag3:"+diag3)

输入

fruit:List(apples, oranges, paers)
nums:List(1, 2, 3, 4)
num2:List(1, 2, 3, 4)
diag3:List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1))

nums和nums2的定义是等价的

该类是后进先出(last-in-first-out, LIFO)、堆栈式访问模式的最佳选择。如果你需要其他的访问模式,例如随机模式或者先进先出(FIFO),可以考虑其他比List更合适的集合。

性能

时间复杂度:对List的prepend,head和tail的 操作,时间复杂度是O(1)。List里面,其他元素个数的操作时间复杂度是O(n)。包括按下表访问元素,‘length’,‘append’和’reverse’

空间复杂度:List实现了tail的结构共享。这意味着很多操作要么是零消耗,要么是衡量内存消耗

    val with4 =    4 :: mainList  // re-uses mainList, costs one :: instance
    val with42 =   42 :: mainList // also re-uses mainList, cost one :: instance
    val shorter =  mainList.tail  // costs nothing as it uses the same 2::1::Nil instances as mainList

创建List的方式与模式匹配

    // Make a list via the companion object factory
    val days = List("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")

    // Make a list element-by-element
    val when = "AM" :: "PM" :: List()

    // Pattern match
    days match {
      case firstDay :: otherDays =>
        println("The first day of the week is: " + firstDay)
        println("The other days of the week are: " + otherDays)
      case List() =>
        println("There don't seem to be any week days.")
    }
    
    val List(a,b) = when
    println("a:" + a)
    println("b:" + b)

输入

The first day of the week is: Sunday
The other days of the week are: List(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
a:AM
b:PM

Note

List已不可变和结构共享为突出特点,因此如果使用正确的话,可以提供较优的性能。
然而,注意,同一个List对象会有多个引用。,这意味着对象依赖于结构共享。序列化/反序列化后,共享的结构没有的,每个引用都会对一个list.

返回的集合类,通常用List[B]来表示,

List的基本操作

list的所有操作可以分为以下三类:

head 返回list的第一个元素
tail 返回一个不包含第一个元素的list
isEmpty 当list为空时返回 true

head和tail的方法只能被非空list使用,当我们使用一个空list的时候,会抛出异常:

scala> Nil.head
java.util.NoSuchElementException: head of empty list

下面是关于list如何被处理,排序的一个简单例子。一种简单的方式是嵌入排序,如下:排序一个非空的list x::xs, 排序剩下的xs,并将第一个元素x放在结果的右边。对空List进行排序会生成空list,代码块如下:

  def isort(xs:List[Int]) : List[Int] = {
    if(xs.isEmpty) Nil
    else insert(xs.head, isort(xs.tail))
  }

  def insert(x: Int, xs:List[Int]) : List[Int] = {
    if(xs.isEmpty || x<=xs.head) x :: xs
    else xs.head :: insert(x, xs.tail)
  }

使用模式匹配来完成插入式排序

  def isort(xs:List[Int]) : List[Int] = {
    /**
      * 模式匹配的方式排序
      */
    xs match {
      case List() => List()
      case x :: xsrest => insert(xs.head, isort(xs.tail))
    }
  }

  def insert(x: Int, xs:List[Int]) : List[Int] = {
    xs match {
      case List() => List(x)
      case xsh :: xsrest => if(x <= xsh) x::xs
      else xsh :: insert(x, xsrest)
    }
  }

通常常,列表上的模式匹配比用方法分解它们更清楚,所以处理列表有限选择模式匹配。

List类的一阶函数

:::

联合两个集合

println((1::1::Nil):::(2::2::Nil))

输出

List(1, 1, 2, 2)

:::在List类里面是一个实现了的方法,也可以手动写一个方法连接两个list.

  /** Adds the elements of a given list in front of this list.
   *  @param prefix  The list elements to prepend.
   *  @return a list resulting from the concatenation of the given
   *    list `prefix` and this list.
   *
   *  @usecase def :::(prefix: List[A]): List[A]
   *    @inheritdoc
   *
   *    Example:
   *    {{{List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)}}}
   */
  def :::[B >: A](prefix: List[B]): List[B] =
    if (isEmpty) prefix
    else if (prefix.isEmpty) this
    else (new ListBuffer[B] ++= prefix).prependToList(this)

上面是List类提供下,new ListBuffer[B] ++= prefix是将List转为ListBuffer
遍历list,元素一次放入ListBuffer中,注意 ListBuffer是mutable的。
实现如下
ListBuffer.scala

  override def ++=(xs: TraversableOnce[A]): this.type = xs match {
    case x: AnyRef if x eq this      => this ++= (this take size)
    case _                           => super.++=(xs)

  }

Growable.scala

  /** ${Add}s all elements produced by a TraversableOnce to this $coll.
   *
   *  @param xs   the TraversableOnce producing the elements to $add.
   *  @return  the $coll itself.
   */
  def ++=(xs: TraversableOnce[A]): this.type = {
    @tailrec def loop(xs: scala.collection.LinearSeq[A]) {
      if (xs.nonEmpty) {
        this += xs.head
        loop(xs.tail)
      }
    }
    xs match {
      case xs: scala.collection.LinearSeq[_] => loop(xs)
      case xs                                => xs foreach +=
    }
    this
  }

case xs: scala.collection.LinearSeq[_] 是判断xs是否是该类型,此处判断的类型只能是参数定义中xs的类型的子集。

使用模式匹配也可以完成的
原理:首先考虑期望输出的形状,然后计算各个部分
的形状,在适当的地方使用算法的递归调用。最后,构建
这些部分的输出。

  def append[T](xs:List[T], ys:List[T]):List[T] = {
    xs match {
      case List() => ys
      case x::xsrest => x::append(xsrest, ys)
    }
  }
  
  println(append((1::2::3::Nil),(4::5::6::Nil)))

输出

List(1, 2, 3, 4, 5, 6)

length

得到list的长度,即元素个数

List和数组不一样,求List的长度需要遍历list,因此花费的时间与list中元素的个数成正比。所以判断一个list是否为空时,使用xs.isEmpty会xs.length

通过tail遍历累计个数

  /** The length of the $coll.
   *
   *  $willNotTerminateInf
   *
   *  Note: the execution of `length` may take time proportional to the length of the sequence.
   */
  def length: Int = {
    var these = self
    var len = 0
    while (!these.isEmpty) {
      len += 1
      these = these.tail
    }
    len
  }

init and last

init返回除去最后一个元素的集合
last集合的最后一个元素

last的实现方式是遍历集合得到最后一个元素

  def last: A = {
    if (isEmpty) throw new NoSuchElementException
    var these = this
    var nx = these.tail
    while (!nx.isEmpty) {
      these = nx
      nx = nx.tail
    }
    these.head
  }

init的实现方式是创建一个Builder对象,Builder可以以增量方式构建集合。先设置集合大小,然后遍历list,依次将元素放入Builder里面

  /** Selects all elements except the last.
   *  $orderDependent
   *  @return  a $coll consisting of all elements of this $coll
   *           except the last one.
   *  @throws UnsupportedOperationException if the $coll is empty.
   */
  def init: Repr = {
    if (isEmpty) throw new UnsupportedOperationException("empty.init")
    var lst = head
    var follow = false
    val b = newBuilder
    b.sizeHint(this, -1)
    for (x <- this) {
      if (follow) b += lst
      else follow = true
      lst = x
    }
    b.result
  }

init和list方法都需要遍历集合,而且需要的时间与集合长度成正比。所以我们在构建数据时,尽量考虑访问时 可以使用list的head方法,而不是last方法。

reverse

得到逆序的集合
利用head和tail遍历,使用::来组成一个逆序的集合。

  override def reverse: List[A] = {
    var result: List[A] = Nil
    var these = this
    while (!these.isEmpty) {
      result = these.head :: result
      these = these.tail
    }
    result
  }

GenSeqLike.scala重写了equals方法

  /** The equals method for arbitrary sequences. Compares this sequence to
   *  some other object.
   *  @param    that  The object to compare the sequence to
   *  @return   `true` if `that` is a sequence that has the same elements as
   *            this sequence in the same order, `false` otherwise
   */
  override def equals(that: Any): Boolean = that match {
    case that: GenSeq[_] => (that eq this.asInstanceOf[AnyRef]) || (that canEqual this) && (this sameElements that)
    case _               => false
  }

所以xs.reverse equals xs.reverse.reverse的值为true
而 xs.reverse eq xs.reverse.reverse的值为false

逆序也能使用:::来实现,如下

  def myReverse[T](xs:List[T]):List[T] = {
    xs match {
      case List() => Nil
      case x::xsrest => myReverse(xsrest):::List(x)
    }
  }

猜你喜欢

转载自blog.csdn.net/licheng989/article/details/89713822