Scala コレクション
[最終更新] (2019/06/03 00:44:06)
最近の投稿
注目の記事

相互変換

object Main {
  def main(args: Array[String]): Unit = {
    // 順序のあるコレクション
    // Traversable -> Iterable -> Seq
    //   -> List
    //   -> Array
    //   -> Buffer -> ListBuffer, ArrayBuffer
    //   -> Vector
    //   -> Stream
    val list = List(1, 2, 3)

    // マップ
    // Traversable -> Iterable -> Map
    val map = Map("a" -> 1, "b" -> 2)

    // 集合
    // Traversable -> Iterable -> Set
    val set = Set(1, 2, 3)


    // 相互変換 (マップのみタプルが絡むため特殊)

    println(map.toList) //=> List((a,1), (b,2))
    println(map.toList.toMap) //=> Map(a -> 1, b -> 2)

    println(list.toIterable) //=> List(1, 2, 3)
    println(list.toSeq) //=> List(1, 2, 3)
    println(list.toList) //=> List(1, 2, 3)
    println(list.toArray) //=> [I@35a9cc1a
    println(list.toSet) //=> Set(1, 2, 3)

    // 生成元がミュータブルであっても、結果はイミュータブルになります。
    val mapImmutable = scala.collection.mutable.Map(1 -> 10).toMap
    // mapImmutable += (2 -> 20) // エラー
  }
}

リスト

基本的なリストの操作は『Scala の基本文法』に記載したとおりです。それ以外にも以下のような操作ができます。

走査

object Main {
  def main(args: Array[String]): Unit = {
    val list = List(1,2,3)
    for(e <- list) {
      println(e)
    }

    list.foreach{ e =>
      println(e)
    }
  }
}

ソート

object Main {
  def main(args: Array[String]): Unit = {
    println(List(3,1,2).sorted) //=> List(1, 2, 3)
    println(List("abc", "ab").sortWith{ (x, y) => x.length < y.length }) //=> List(ab, abc)
    println(List("abc", "ab").sortBy{ _.length }) //=> List(ab, abc)
  }
}

ネストしたリストをフラット化 (または Option 型のリストの処理)

object Main {
  def main(args: Array[String]): Unit = {
    // ruby の flatten のようなものです
    println(List(List(1,2),List(3)).flatten) //=> List(1, 2, 3)

    println(List(1,2,3).flatMap { e => // とすると、
      List(e, e)
    })
    //=> List(1, 1, 2, 2, 3, 3)
    println(List(1,2,3).map { e => // これと同じ意味。
      List(e,e)
    }.flatten) // 中間 List も生成せずに高速
    //=> List(1, 1, 2, 2, 3, 3)

    // Option 型のリストから None を除外する効果
    println(List(Some("a"), Some("b"), None).flatten) //=> List(a, b)
  }
}

文字列化 (Join)

object Main {
  def main(args: Array[String]): Unit = {
    val list = List(1,2,3)
    println(list.mkString) //=> 123
    println(list.mkString("delimiter")) //=> 1delimiter2delimiter3
    println(list.mkString("pre", "delimiter", "post")) //=> pre1delimiter2delimiter3post
  }
}

集約

object Main {
  def main(args: Array[String]): Unit = {
    // ruby の inject のようなものです
    println(List("b", "c").foldLeft("a") { (l, e) =>
      l + e
    }) //=> abc ("a" は l の初期値)

    println(List("a", "b").foldRight("c") { (e, r) =>
      e + r
    }) //=> abc ("c" は r の初期値)

    // (("a" + "b") + "c")
    println(List("a", "b", "c").reduceLeft { (l, e) =>
      l + e
    }) //=> abc

    // ("a" + ("b" + "c"))
    println(List("a", "b", "c").reduceRight { (e, r) =>
      e + r
    }) //=> abc

    // リストが空 List() の場合に例外を投げたくない場合
    println(None.reduceLeftOption { (l: Any, e: Any) =>
    }) //=> None
    println(List(1,2,3).reduceRightOption { (e: Int, r: Int) =>
      e + r
    }) //=> Some(6)
  }
}

分割

object Main {
  def main(args: Array[String]): Unit = {
    // 条件式 (true/false) で二つのリストに仕分け
    println(List(-1,2,-3).partition{ _ > 0 }) //=> (List(2),List(-1, -3))

    // 式の結果で複数のリストに仕分け
    println(List(-1,2,-3).groupBy{ _ > 0 }) //=> Map(false -> List(-1, -3), true -> List(2))

    // インデックス番号 i で二つのリスト (0..i-1, i..-1) に分割
    val (listA, listB) = List("a", "b", "c").splitAt(2)
    println(listA) //=> List(a, b)
    println(listB) //=> List(c)
  }
}

Zip (タプルを利用した処理)

object Main {
  def main(args: Array[String]): Unit = {
    // ruby の each_with_index のようなものです
    List("a", "b", "c").zipWithIndex.foreach{ tuple =>
      println(tuple) //=> (a,0), (b,1), (c,2)
    }

    // 長さが一致しない場合、超過した要素は捨てられます
    val zipped = List(1,2,3,4).zip(List("a", "b", "c"))
    val unzipped = zipped.unzip
    println(zipped) //=> List((1,a), (2,b), (3,c))
    println(unzipped) //=> (List(1, 2, 3),List(a, b, c))
  }
}

集合

昔、数学 A で出会った集合です。

object Main {
  def main(args: Array[String]): Unit = {
    val set = Set(1,2,3)

    // 要素の追加
    val set2 = set + 4
    println(set2) //=> Set(1, 2, 3, 4)
    println(set2 + 4) //=> Set(1, 2, 3, 4) (重複して追加はされない)

    // 要素の削除
    println(set - 1) //=> Set(2, 3)
    println(set - 0) //=> Set(1, 2, 3) (存在しない要素を指定してもエラーにはならない)
    println(set.filter{ _ != 1 }) //=> Set(2, 3)

    // 和集合
    println(set | Set(4,5,6)) //=> Set(5, 1, 6, 2, 3, 4) ←(A ∪ B)
    println(set ++ Set(4,5,6)) // としても Set では同じ意味

    // 積集合
    println(Set(1,2) & Set(2)) //=> Set(2) ←(A ∩ B)

    // 差集合
    println(set -- Set(1)) //=> Set(2, 3)

    // 走査
    set.foreach{ e =>
      println(e) //=> 1 => 2 => 3
    }

    // 変換
    println(set.map{ _ * 2 }) //=> Set(2, 4, 6)
  }
}

マップ

他の言語の連想配列のようなものです。

object Main {
  def main(args: Array[String]): Unit = {

    val map = Map[Int, Int](1 -> 10, 2 -> 20)

    // 値の取得
    println(map.get(1)) //=> Some(10)
    println(map(2)) //=> 20 (ただし、存在しないキーを指定すると例外)
    println(map.getOrElse(0, -1)) //=> -1

    // マップはタプルのリストのようなもの
    val tuple1 = (1, 10)
    val tuple2 = (2, 20)
    val map2 = Map(tuple1, tuple2) // 1 -> 2 は (1, 2) というタプルを生成する記法
    println(map2) //=> Map(1 -> 10, 2 -> 20)

    // 要素の追加
    val map3 = map + (3 -> 30, 4 -> 40)
    println(map3) //=> Map(1 -> 10, 2 -> 20, 3 -> 30, 4 -> 40)

    // マップの連結
    println(Map(1->10) ++ Map(2->20)) //=> Map(1 -> 10, 2 -> 20)

    // 要素の削除
    println(Map(1->10, 2->20) - 1) //=> Map(2 -> 20)
    println(Map(1->10, 2->20, 3->30, 4->40) - (1,3,4)) //=> Map(2 -> 20)
    println(Map(1->10, 2->20, 3->30, 4->40) -- List(1,3,4)) //=> Map(2 -> 20)
    println(Map(1->10,2->20).filter{ case(key,value) =>
      key == 2
    }) //=> Map(2 -> 20)

    // 走査
    map.foreach{ e =>
      println(e._1 + e._2) //=> 11 => 22 (タプルのループ)
    }
    map.foreach{ case(key, value) =>
      println(key + value) // としても同じ
    }
    println(map.keys) //=> Set(1, 2)
    println(map.values) //=> MapLike(10, 20)

    // 長さ
    println(map.size) //=> 2

    // 含まれるか、空かどうか
    println(map.contains(0)) //=> false
    println(map.isEmpty) //=> false
    println(map.nonEmpty) //=> true

    // 変換 (map の map と書くと混乱する)
    println(Map(1->10, 2->20).map{ case(key,value) =>
      (key, value * 2)
    }) //=> Map(1 -> 20, 2 -> 40)
    println(Map(1->10, 2->20).map{ case(key,value) =>
      key
    }) //=> List(1, 2)

    // ソート (キーを List に変換してソート)
    val sortedKeys = map.keys.toList.sorted
    sortedKeys.foreach{ key =>
      println(map(key)) //=> 10 => 20
    }
  }
}

ストリーム

参考: 具象不変コレクションクラス

ストリーム (Stream) はリストに似ているが、要素は遅延評価される。そのため、ストリームは無限の長さをもつことができる。呼び出された要素のみが計算される。他の点においては、ストリームはリストと同じ性能特性をもつ。

object Main {
  def main(args: Array[String]): Unit = {

    // a, b, a+b, b+(a+b),...
    def fibFrom(a: Long, b: Long): Stream[Long] = {
      a #:: fibFrom(b, a + b) // ストリームの先頭への要素の追加
    }

    val fibs = fibFrom(1,1) // 無限長
    println(fibs) //=> Stream(1, ?)

    for(i <- 0 to 10) {
      println(fibs(i))
    }

    // リストでは実現できません。
    def fibFromList(a: Long, b: Long): List[Long] = {
      a +: fibFromList(b, a + b) // リストの先頭への要素の追加
    }
    // val fibsList = fibFromList(1,1) // 無限長リストは無限にメモリを必要とする
    //=> java.lang.StackOverflowError
  }
}

その他の話題

この続きが気になる方は
関連ページ
    概要 Java のコレクションは以下の何れかのインターフェースを実装しています。 java.util.List java.util.Set java.util.Map それらとは別の概念として、リストに似たデータ構造の「配列」があります。 List と異なり、サイズを変更できません。 List と異なり、基本型の配列を宣言できます。
    whileループ sample.py #!/usr/bin/python # -*- coding: utf-8 -*- x = 10 while x: print x, # カンマをつけると改行せずに空白区切りで出力 x -= 1 # pythonにはデクリメント演算子が存在しない (インクリメント演算子もない) else: print x # while判定が偽の後に実行
    概要 Scala は JVM 上で動作するバイトコードにコンパイルできる言語です。JAVA よりも柔軟な記述ができます。事前にこちらからダウンロードおよびインストールしておいてください。基本的な文法をまとめます。 変数および定数 詳細は『Scala の型に関する知識』をご参照ください。 object HelloWorld { def main(args: Array[String]):