Scala提供了一系列的集合类的实现。同时,它对于集合类型也进行了一些抽象。这就使得你可以操作一个集合的Foo
对象,而不用去关心这个集合是一个List
,Set
还是其他的什么。
这个网页提供了一个很好的方式来理解scala里的集合的默认实现,并且都链接到了相应的scaladoc。
- 基本集合类 常用的集合类型
- 层级关系 集合类的抽象关系
- 集合方法
- 可变性
- Java 集合类也可直接使用
基本集合类
List
标准的linked list。
scala> List( 1 , 2 , 3 )
res0: List[Int] = List( 1 , 2 , 3 )
你也可以像在函数式语言里一样把它们串接起来。
scala> 1 :: 2 :: 3 :: Nil
res1: List[Int] = List( 1 , 2 , 3 )
参考 API文档
Set
Set里不包含重复元素
scala> Set( 1 , 1 , 2 )
res2: scala.collection.immutable.Set[Int] = Set( 1 , 2 )
参考 API文档
Seq
Sequence都有一个预定义的顺序。
scala> Seq( 1 , 1 , 2 )
res3: Seq[Int] = List( 1 , 1 , 2 )
(注意返回的结果是一个List。Seq
是一个trait;List是它的一个实现类。Seq
对象是一个工厂对象,正如你所看到的,它会创建一个List。)
参考 API文档
Map
Map是保存键-值对的容器。
scala> Map( 'a' -> 1 , 'b' -> 2 )
res4: scala.collection.immutable.Map[Char,Int] = Map((a, 1 ), (b, 2 ))
参考 API文档
层级关系
上面的这些都是trait,在可变(mutable)包和不可变(immutable)包里都有对应的实现,同时也有其他特殊用途的实现。
Traversable
所有的集合都可遍历的。这个trait定义了标准函数组合器。这些组合器都是通过foreach这个方法来实现的,并且所有的集合都实现了这个方法。
参考 API文档
Iterable
这个trait有一个iterator()
方法,它返回一个可以遍历所有元素的Iterator。 参考 API文档
Seq
一组有序的元素。
参考 API文档
Set
一组没有重复元素的集合。
参考 API文档
Map
键-值对。
参考 API文档
方法
Traversable
上面所有的方法在子类中都是可用的。参数和返回值可能会改变,因为子类可以覆盖它们。
def head : A
def tail : Traversable[A]
这里我们可以定义函数组合器。
def map [B] (f: (A) => B) : CC[B]
上面的代码返回一个集合,里面的每一个元素都通过函数f
进行了转换。def foreachU: Unit
对一个集合里的每一个元素都执行函数f
。def find (p: (A) => Boolean) : Option[A]
它返回第一个符合断言函数的元素。
def filter (p: (A) => Boolean) : Traversable[A]
这个返回一个包含所有符合断言函数的元素的集合。
切分:
def partition (p: (A) ⇒ Boolean) : (Traversable[A], Traversable[A])
通过一个断言函数把一个集合拆成两份
def groupBy [K] (f: (A) => K) : Map[K, Traversable[A]]
转换:
有趣的是,集合之间可以相互进行转换。
def toArray : Array[A]
def toArray [B >: A] (implicit arg0: ClassManifest[B]) : Array[B]
def toBuffer [B >: A] : Buffer[B]
def toIndexedSeq [B >: A] : IndexedSeq[B]
def toIterable : Iterable[A]
def toIterator : Iterator[A]
def toList : List[A]
def toMap [T, U] (implicit ev: <:<[A, (T, U)]) : Map[T, U]
def toSeq : Seq[A]
def toSet [B >: A] : Set[B]
def toStream : Stream[A]
def toString () : String
def toTraversable : Traversable[A]
我们可以把一个Map转换成一个数组,然后得到一个键值对数组。
scala> Map( 1 -> 2 ).toArray
res41: Array[(Int, Int)] = Array(( 1 , 2 ))
Iterable
这个接口给你提供了一个迭代器iterator。
def iterator: Iterator[A]
Iterator提供了什么操作呢?
def hasNext(): Boolean
def next(): A
这个是非常‘Java式’的用法。在Scala中,你很少会见到使用iterator的地方,大部分的时候你看到的都是函数组合器以及for循环语句。
Set
def contains(key: A): Boolean
def +(elem: A): Set[A]
def -(elem: A): Set[A]
Map
一序列键值对,提供按key进行查询的操作。 可以通过给apply()方法传入一个键值对的列表来创建Map:
scala> Map( "a" -> 1 , "b" -> 2 )
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((a, 1 ), (b, 2 ))
或者按照下面的方式:
scala> Map(( "a" , 2 ), ( "b" , 2 ))
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((a, 2 ), (b, 2 ))
题外话
->
是什么呢?这是一个特殊的语法,它是一个返回值为元组的方法。
scala> "a" -> 2
res0: (java.lang.String, Int) = (a, 2 )
记住,它只是下面这种形式的语法糖:
scala> "a" .->( 2 )
res1: (java.lang.String, Int) = (a, 2 )
你也可以通过++
来构造Map
scala> Map.empty ++ List(( "a" , 1 ), ( "b" , 2 ), ( "c" , 3 ))
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((a, 1 ), (b, 2 ), (c, 3 ))
常用的集合子类
HashSet 和 HashMap 支持快速查找的最常用的集合类。HashSet API, HashMap API
TreeMap SortedMap的子类,它提供有序的访问方式。 TreeMap API
Vector 支持快速查找和更新 Vector API
scala> IndexedSeq( 1 , 2 , 3 )
res0: IndexedSeq[Int] = Vector( 1 , 2 , 3 )
Range 有序的通过空格分隔的Int序列。 在之前很多用来计数的for循环经常使用它。Range API
scala> for (i <- 1 to 3 ) { println(i) }
1
2
3
Range也支持标准的函数组合器。
scala> ( 1 to 3 ).map { i => i }
res0: scala.collection.immutable.IndexedSeq[Int] = Vector( 1 , 2 , 3 )
默认实现
在trait上使用apply方法会得到该trait的一个默认实现,例如,Iterable(1,2)返回一个List作为它的默认实现。
scala> Iterable( 1 , 2 )
res0: Iterable[Int] = List( 1 , 2 )
Seq也是这样的,我们之前见过
scala> Seq( 1 , 2 )
res3: Seq[Int] = List( 1 , 2 )
scala> Iterable( 1 , 2 )
res1: Iterable[Int] = List( 1 , 2 )
scala> Sequence( 1 , 2 )
warning: there were deprecation warnings; re-run with -deprecation for details
res2: Seq[Int] = List( 1 , 2 )
Set
scala> Set( 1 , 2 )
res31: scala.collection.immutable.Set[Int] = Set( 1 , 2 )
一些描述性的trait
IndexedSeq 支持对元素进行快速随机访问以及快速的length操作 API doc
LinearSeq 只对第一个和最后一个元素支持快速访问。 API doc
可变性 vs. 不可变性
不可变性
优点
- 在多线程场景下不会被改变
缺点
- 没有办法进行修改
Scala允许我们根据实际需求决定可变性,它鼓励我们只用不可变的对象,但是也不禁止我们使用具有可变性的对象。这个和var与val的场景非常相似。我们一开始都是使用val,当实际需要的时候会改成var。
可变集合
我们前面讨论的所有的类都是不可变的。我们现在来讨论一下常用的可变集合。
HashMap 定义了getOrElseUpdate
, +=
HashMap API
scala> val numbers = collection.mutable.Map( 1 -> 2 )
numbers: scala.collection.mutable.Map[Int,Int] = Map(( 1 , 2 ))
scala> numbers.get( 1 )
res0: Option[Int] = Some( 2 )
scala> numbers.getOrElseUpdate( 2 , 3 )
res54: Int = 3
scala> numbers
res55: scala.collection.mutable.Map[Int,Int] = Map(( 2 , 3 ), ( 1 , 2 ))
scala> numbers += ( 4 -> 1 )
res56: numbers.type = Map(( 2 , 3 ), ( 4 , 1 ), ( 1 , 2 ))
ListBuffer和ArrayBuffer 定义了+=
操作符 ListBuffer API, ArrayBuffer API
LinkedList和DoubleLinkedList LinkedList API, DoubleLinkedList API
PriorityQueue API doc
Stack和ArrayStack Stack API, ArrayStack API
StringBuilder 有趣的是StringBuilder是竟然一个集合 API doc
和Java进行交互
你可以通过 JavaConverters 包在Java和Scala的集合类之间进行转换. 它给常用的Java集合提供了asScala
方法,同时给常用的Scala集合提供了asJava
方法。
import scala.collection.JavaConverters._
val sl = new scala.collection.mutable.ListBuffer[Int]
val jl : java.util.List[Int] = sl.asJava
val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
assert (sl eq sl2)
双向转换:
scala.collection.Iterable <=> java.lang.Iterable
scala.collection.Iterable <=> java.util.Collection
scala.collection.Iterator <=> java.util.{ Iterator, Enumeration }
scala.collection.mutable.Buffer <=> java.util.List
scala.collection.mutable.Set <=> java.util.Set
scala.collection.mutable.Map <=> java.util.{ Map, Dictionary }
scala.collection.mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap
另外,下面提供了一些单向的转换方法:
scala.collection.Seq => java.util.List
scala.collection.mutable.Seq => java.util.List
scala.collection.Set => java.util.Set
scala.collection.Map => java.util.Map