Scala学習【コレクション】

最近Scalaをはじめたので、学習した内容をブログに書いていけば、知識が補強されて理解が深まるのでは?と思い書いていきます。 今回はScalaの豊富なコレクションライブラリーについて簡単に特徴をまとめました。おそらく基本的なものしか網羅できてません。

シーケンス

Seq型はただのトレイトであり、実際の実装は複数存在している

List型

  • 先頭の値(head)とその後ろのリスト(tail)により構成される
  • 不変
  • 先頭の値の取得や削除は高速だが、任意の添字を持つ要素へのアクセスは、リストを線形に辿っていくため、高速ではない
  • 先頭の値の取得や削除は高速なので、パターンマッチに適している
scala> List(1, 2, 3)
res0: List[Int] = List(1, 2, 3)

scala> 3::res0
res1: List[Int] = List(3, 1, 2, 3)

Array型

  • Javaの配列と互換性を持った型
  • 可変
  • Javaの配列は、宣言時にメモリの確保を行う。
  • そして、添字で数値を指定してそのメモリ上の値を取得するため、添字を使った特定の要素の参照は、一度のアクセスで行うことができ高速
  • 一方挿入削除を行う際は、再度配列のメモリの確保を行うのでメモリ効率は悪くなる
scala> Array(1, 2, 3)
res3: Array[Int] = Array(1, 2, 3)

scala> res3(1)
res4: Int = 2

ListBuffer

  • 可変
  • 要素を先頭に挿入するときも末尾に追加するときも一定時間で処理できるリスト
scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer

scala> val buf = new ListBuffer[Int]
buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

scala> buf += 1
res6: buf.type = ListBuffer(1)

scala> buf += 2
res7: buf.type = ListBuffer(1, 2)

scala> 3 +=: buf
res8: buf.type = ListBuffer(3, 1, 2)

ArrayBuffer

  • 可変
  • 配列の先頭と末尾で要素を追加・削除できる
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> val buf = new ArrayBuffer[Int]()
buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> buf += 12
res9: buf.type = ArrayBuffer(12)

scala> buf += 15
res10: buf.type = ArrayBuffer(12, 15)

scala> 3 +=: buf
res11: buf.type = ArrayBuffer(3, 12, 15)

Range

  • 範囲を表すオブジェクト
scala> 1 to 3
res12: scala.collection.immutable.Range.Inclusive = Range 1 to 3

scala> 1 to 17 by 4
res14: scala.collection.immutable.Range = Range 1 to 17 by 4

scala> 1 until 3

文字列(StringOps)

  • Predef(Scalaソースファイルに暗黙のうちにimportされているオブジェクト)がStringからStringOpsへ暗黙の型変換を提供しているため、すべての文字列はシーケンスのように扱うことが出来る。
scala> val s = "hoge"
s: String = hoge

scala> s(1)
res5: Char = o

集合

Set

  • 要素の集合を表す型
  • 要素は重複しない
  • 可変と不変があり、デフォルトは不変
  • 順番を維持しない(順序が必要な際はTreeSetを用いる)
scala> val s = Set(1, 1, 2, 2, 3, 3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

連想配列

Map

  • キーと値を持つ連想配列
  • 可変と不変があり、デフォルトは不変
  • キーと値のセットは2要素のタプルで表現されている
  • 添字を使って中身の値を取得することができるが、nullが発生する可能性があるため、基本はgetメソッドを利用してOptionの値を取得する
  • 順番を維持しない(順序が必要な際はTreeMapを用いる)
scala> val m = Map("a" -> 1, "b" -> 2, "c" -> 3)
m: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3)

コレクション

Stack

  • 後入れ先出しのコレクション
  • 可変と不変がある
  • pushメソッドで要素を入れ、pop2メソッドで先頭の値と残った要素を持つStackをタプルとして取得できる
scala> scala.collection.immutable.Stack()
res0: scala.collection.immutable.Stack[Nothing] = Stack()

scala> res0.push(1)
res1: scala.collection.immutable.Stack[Int] = Stack(1)

scala> res1.push(2)
res2: scala.collection.immutable.Stack[Int] = Stack(2, 1)

scala> res2.pop2
res3: (Int, scala.collection.immutable.Stack[Int]) = (2,Stack(1))

Queue

  • 先入れ先出しのコレクション
  • 可変と不変がある
  • enqueueメソッドで要素を入れ、dequeueメソッドで先頭の値と残った要素を持つQueueをタプルとして取得できる
scala> scala.collection.immutable.Queue()
res4: scala.collection.immutable.Queue[Nothing] = Queue()

scala> res4.enqueue(1)
res5: scala.collection.immutable.Queue[Int] = Queue(1)

scala> res5.enqueue(2)
res6: scala.collection.immutable.Queue[Int] = Queue(1, 2)

scala> res6.dequeue
res7: (Int, scala.collection.immutable.Queue[Int]) = (1,Queue(2))

PriorityQueue

  • enqueueした順によらず、自動的に中身をソートして並べてくれるQueue(優先度つきキュー)
  • 可変のみ
  • 挿入削除時に並べ替えるコストが低く、先頭の値取得も高速に行うことができる
  • newBuilderメソッドの引数にOrdering型のオブジェクトを渡すことでソートする順番を変更できる
  • ソートにコストがかかり、先頭の値だけを取得すれば良いような処理を、非常に高速に処理することができるコレクション

タプル

  • 異なる型のオブジェクトを結合して、1単位として渡せるようにしたもの
scala> (1, "hello", Console)
res21: (Int, String, Console.type) = (1,hello,scala.Console$@2de22e7)

参考

Scalaスケーラブルプログラミング第3版 - インプレスブックス

N予備校 プログラミングコース