Collections

immutable

수정 불가능한 콜렉션은 절대 변하지 않아서 안전하게 이에 대한 레퍼런스를 공유하고 멀티스레드 프로그래밍도 가능.

scala.collection.mutable.Map : 수정 가능한 콜렉션
scala.collection.immutable.Map : 수정 불가능한 콜렉션
위 두개의 Map 모두 scala.collection.Map을 가짐.
scala.collection 패키지의 컴패니언 오브젝트는 수정 불가능한 콜렉션을 생성한다.
scala, Predef 오브젝트는 수정 불가능한 트레이트를 지칭하는 List, Set, Map 타입 별칭을 갖고 있다.

// 정수의 모든 자리 숫자의 집합 계산
def digits(n: Int): Set[Int] =
  if (n < 0) digits(-/n)
  else if (n < 10) Set(n)
  else digits(n / 10) + (n % 10)

위 메소드는 하나의 숫자를 포함한 집합으로 시작. 각 단계에서 다른 숫자가 더해짐.
하지만 숫자 더하기가 집합을 변경하지는 않으며, 대신 각 단계에서 새로운 집합이 생성된다.


Sequence

  • Vector
    ArrayBuffer의 수정 불가능한 버전.
    빠른 무작위 접근이 가능한 인덱스 시퀀스.
    벡터는 각 노드가 32개까지 자식을 가질 수 있는 트리로 구현됨.

  • Range
    정수 시퀀스를 나타냄.
    Range 오브젝트는 모든 시퀀스 값을 저장하지는 않고, 시작, 끝, 증가분만 저장. to와 until 메소드로 Range 오브젝트 생성.


List

val digits = List(4, 2)

digits.head     // 4
digits.tail     // List(2)
digits.tail.head  // 2
digits.tail.tail  // Nil

// List(9, 4, 2)
9 :: List(4, 2)
9 :: 4 :: 2 :: Nil
9 :: (4 :: (2 :: Nil))

// 재귀 호출을 이용해 모든 원소의 합 계산
def sum(lst: List[Int]): Int =
  if (lst == Nil) 0 else lst.head + sum(lst.tail)

// 패턴매칭 사용
def sum(lst: List[Int]): Int = lst match {
  case Nil => 0
  case h :: t => h + sum(t) // h는 lst.head, t는 lst.tail
}

// 이미 sum 함수는 있다.
List(9, 4, 2).sum   // 15를 돌려준다.


mutable

LinkedList는 elem 레퍼런스에 대입하여 머리를 수정할 수 있고, next 레퍼런스에 대입하여 꼬리를 수정할 수 있다는 점을 제외하면 수정 불가능한 List와 유사하게 동작한다.

// 모든 음의 값을 0으로 변경
val lst = scala.collection.mutable.LinkedList(1, -2, 7, -9)
var cur = lst
while (cur != Nil) {
  if (cur.elem < 0)  cur.elem = 0
  cur = cur.next
}
// 리스트에서 매 두 번째 원소를 제거
var cur = lst
while (cur != Nil && cur.next != Nil) {
  cur.next = cur.next.next
  cur = cur.next
}

링크드리스트에서 리스트 노드를 리스트의 마지막 노드로 만들려면, next 레퍼런스를 Nil로 설정할 수 없다. 대신 LinkedList.empty로 설정해야 한다. null로 설정하지 말아야 한다.


Set (집합)

서로 다른 원소의 콜렉션

Set(2, 0, 1) + 1    // Set(2, 0, 1)과 동일

삽입된 순서를 보존하지 않는다.
원소가 hashCode 메소드의 값에 따라 정리되는 해시 집합으로 구현된다.
해시 집합에서 원소를 찾는 것은 배열이나 리스트에서 찾는 것 보다 훨씬 빠르다.


링크드 해시 집합은 원소가 삽입된 순서를 기억한다. 이 용도로 링크드 리스트를 유지한다.

val weekdays = scala.collection.mutable.LinkedHashSet("Mo", "Tu", "We", "Th", "Fr")
scala.collection.immutable.SortedSet(1, 2, 3, 4, 5, 6)  // SortedSet : 정렬된 순서로 iterate

정렬된 집합은 레드-블랙 트리로 구현
데이터 구조에 대한 책 http://www.cs.williams.edu/javastructures/Welcome.html


비트 집합은 음수가 아닌 정수의 집합을 일련의 비트로 구현한 것.
i가 집합에 있으면 i번째 비트가 1이 된다.
BitSet 클래스는 수정 가능한 클래스, 수정 불가능한 클래스 모두 제공한다.

contains 메소드는 집합이 주어진 값을 가지고 있는지 확인한다.
subsetOf 메소드는 집합의 모든 원소가 다른 집합에 속하는지 확인한다.

val digits = Set(1, 7, 2, 9)
digits contains 0     // false
Set(1, 2) subsetOf digits // true

union, intersect, diff 메소드로 집합 연산 수행 가능.
|, &, %~, ++, -- 연산자 사용 가능.


원소들을 추가 혹은 제거하는 연산자

연산자 설명 콜렉션 타입
coll :+ elem coll과 같은 타입의 콜렉션에 elem이 뒤에 추가. Seq
elem +: coll coll과 같은 타입의 콜렉션에 elem이 앞에 추가. Seq
coll + elem
coll + (e1, e2, …)
coll과 같은 타입의 콜렉션에 주어진 원소가 더해짐. Set, Map
coll - elem
coll - (e1, e2, …)
coll과 같은 타입의 콜렉션에 주어진 원소가 제거됨. Set, Map, ArrayBuffer
coll ++ coll2
coll2 ++: coll
coll과 같은 타입의 콜렉션에 양쪽 콜렉션의 원소들을 포함. Iterable
coll – coll2 coll과 같은 타입의 콜렉션에 coll2의 원소들이 제거됨.
(sequence의 경우, diff를 사용.)
Set, Map, ArrayBuffer
elem :: lst
lst2 ::: lst
원소 혹은 주어진 리스트 lst 앞에 추가된 리스트. +:와 ++:와 같음. List
list ::: list2 list ++: list2와 같음. List
set | set2 합집합. Set
set & set2 교집합. Set
set &~ set2 차집합. Set
coll += elem
coll += (e1, e2, …)
coll ++= coll2
주어진 원소들을 추가하여 coll을 수정. Mutable
collections
coll -= elem
coll -= (e1, e2, …)
coll –= coll2
주어진 원소들을 제거하여 coll을 수정. Mutable
collections
elem +=: coll
coll2 ++=: coll
주어진 원소나 콜렉션을 앞에 추가하여 coll을 수정. ArrayBuffer


common method

method description
head 첫 원소를 리턴.
last 마지막 원소를 리턴.
headOption 첫 원소를 option으로 리턴.
lastOption 마지막 원소를 option으로 리턴.
tail 첫 원소를 제외한 모두를 리턴.
init 마지막 원소를 제외한 모두를 리턴.
length 길이를 리턴.
isEmpty 길이가 0이면 true를 리턴.
map(f), foreach(f), flatMap(f), collect(pf) 함수를 모든 원소에 적용.
reduceLeft(op), reduceRight(op),
foldLeft(init)(op), foldRight(init)(op)
이항 연산을 모든 원소에 주어진 순서로 적용.
reduce(op), fold(init)(op),
aggregate(init)(op, conbineOp)
이항 연산을 임의의 순서로 적용
sum 합(원소 타입이 암묵적으로 Numeric 트레이트로 변환될 수 있다는 가정하에) 리턴.
product 곱(원소 타입이 암묵적으로 Numeric 트레이트로 변환될 수 있다는 가정하에) 리턴.
max 최대(원소 타입이 Ordered 트레이트로 변환될 수 있다는 가정하에).
min 최소(원소 타입이 Ordered 트레이트로 변환될 수 있다는 가정하에).
count(pred) 조건을 만족하는 원소들의 숫자를 리턴.
forall(pred) 모든 원소가 조건을 만족하면 true.
exists(pred) 하나의 원소가 조건을 만족하면 true.
filter(pred) 조건을 만족하는 모든 원소를 리턴.
filterNot(pred) 조건을 만족하지 않는 모든 원소를 리턴.
partition(pred) 조건을 만족하는 원소들과 만족하지 않는 원소들을 분리하여 pair로 제공.
takeWhile(pred) pred를 만족하는 첫번째 원소를 리턴.
dropWhile(pred) pred를 만족하는 원소 중 첫번째 원소를 제거하고 리턴.
span(pred) takeWhile과 dropWhile의 결과값을 pair로 리턴.
take(n) 첫 n개의 원소를 리턴.
drop(n) 첫 n개를 제외한 모든 원소를 리턴.
splitAt(n) take와 drop의 결과값을 pair로 리턴.
takeRight(n) 마지막 n개의 원소를 리턴.
dropRight(n) 마지막 n개를 제외한 모든 원소 리턴.
slice(from, to) from에서 to 범위의 원소를 리턴.
zip(coll2),
zip(coll2, fill, fill2),
zipWithIndex
이 콜렉션과 다른 콜렉션 원소의 쌍들을 리턴.
grouped(n) 원소를 n개 단위로 묶은 iterator 제공.
sliding(n) 1 to n, 2 to n+1 형태로 원소를 n개 단위로 묶은 이터레이터 제공.
mkString(before, between, after) 주어진 문자열을 첫 번째 전, 각 원소 사이, 마지막 원소 후에 추가하여 모든 원소의 문자열을 만듬.
addString(sb, before, between, after) 모든 원소의 문자열을 만든 후 문자열 빌더에 추가.
toIterable, toSeq, toIndexedSeq
toArray, toList, toStream
toSet, toMap
콜렉션을 지정된 타입의 콜렉션으로 변환.
copyToArray(arr),
copyToArray(arr, start, length),
copyToBuffer(buf)
원소들을 배열 혹은 버퍼로 복사.


folding

val freq = scala.collection.mutable.Map[Char, Int]()
for (c <- "Mississippi") freq(c) = freq.getOrElse(c, 0) + 1
// freq : Map(M -> 1, s -> 4, p -> 2, i -> 4)
(Map[Char, Int]() /: "Mississippi") {
  (m, c) => m + (c -> (m.getOrElse(c, 0) + 1))
}
// This returns immutable map : Map(M -> 1, i -> 4, s -> 4, p -> 2)


scan

모든 중간 결과의 콜렉션을 얻는다.

(1 to 10).scanLeft(0)(_ + _)
// result : Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)


zipping

val prices = List(5.0, 20.0, 9.95)
val quantities = List(10, 2, 1)

prices zip quantities
// result : List[(Double, Int)] = List((5.0,10), (20.0,2), (9.95,1))

(prices zip quantities) map { p => p._1 * p._2 }
// result : List[Double] = List(50.0, 40.0, 9.95)

((prices zip quantities) map { p => p._1 * p._2 }) sum
// result : Double = 99.95


iterator

while (iter.hasNext)
  iter.next()

for (elem <- iter)
  elem


stream

스트림은 꼬리가 lazy하게 계산됨.
#:: 연산자를 통해 스트림 생성.

def numsFrom(n: BigInt) : Stream[BigInt] = n #:: numsFrom(n + 1)
val tenOrMore = numsFrom(10)  // result : tenOrMore: Stream[BigInt] = Stream(10, ?)


자바 콜렉션과의 상호 호환

자바 콜렉션을 사용하려면, 스칼라와 자바 콜렉션 사이 변환을 제공하는 JavaConversions 오브젝트를 가져온다.

import scala.collection.JavaConversions._
val props: scala.collection.mutable.Map[String, String] = System.getProperties()
props("com.horstmann.scala") = "impatient"  // put("com.hosrtmann.scala", "impatient") 호출


thread safe Collections

여러 스레드에서 수정 가능한 콜렉션에 접근 할 때, 다른 쓰레드들에서 접근하는 것과 동시에 한 쓰레드에서 콜렉션을 변경하지 않도록 해야 할 필요가 있다.

// 동기화된 연산의 맵 생성
val scores = new scala.collection.mutable.HashMap[String, Int] with
  scala.collection.mutable.SynchronizedMap[String, Int]

혹은 java.util.concurrent 패키지의 클래스 사용. 데이터 구조와 관련있는 부분만 동기화하고, 관련 없는 부분은 다른 스레드에서 접근 가능.


병렬 콜렉션

par 메소드 사용

coll.par.sum  // coll: collection name. 합을 병렬로 계산

여기서 리턴되는 병렬 콜렉션은 ParIterable의 서브 타입. 순차컬렉션으로 변환하려면 ser 메소드 사용.