17.1 시퀀스

: 시퀀스 타입은 순서가 정해진 데이터 그룹을 가지고 작업할 수 있게 해준다.

 

< 리스트 >

: 변경 불가능한 연결 리스트로 원소를 앞 부분에 빠르게 추가/삭제할 수 있지만 리스트를 순차적으로 따라가야해 임의의 위치에 접근할 때 빠르지 않다. 

: 첫 번째 원소를 빠르게 추가/삭제할 수 있는 건 패턴 매치를 잘할 수 있다는 뜻

: 불변성은 리스트를 복사하지 않아도 되기 때문에 효율적이면서 올바른 알고리즘 개발에 도움

 

< 배열 >

: 임의의 위치에 있는 원소에 효율적으로 접근

: 배열은 '[]'가 아니라 '()'에 인덱스를 넣어서 원소 접근

 

< 리스트 버퍼 >

: 변경 가능한 객체로 상수 시간에 원소를 앞/뒤로 추가 가능 

    * 리스트로 끝부분에 원소를 추가하려면 리스트의 앞에 원소를 차례로 추가해 뒤집힌 리스트를 만들고 reverse 호출

- append , "+=" : 원소를 뒤에 추가

- prepend, "+=:"  : 연산자를 통해 원소를 앞에 추가

- toList : ListBuffer => List

- 잠재적인 스택 오버플로를 피하기 위해 List 대신 ListBuffer 사용(재귀, for, while에서)

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                                  
res4: buf.type = ListBuffer(1)

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

scala> buf     
res6: scala.collection.mutable.ListBuffer[Int]
  = ListBuffer(1, 2)

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

scala> buf.toList
res8: List[Int] = List(3, 1, 2)

 

< 배열 버퍼 >

: 끝 부분과 시작 부분에 원소를 추가/삭제할 수 있다는 점만 제외하면 배열과 같다.

: 새 원소 추가/삭제하는 데 평균적으로 상수시간이 걸리나, 때때로 버퍼의 내용을 저장하기 위해 새로운 배열을 할당해야 하기 때문에 종종 선형 시간이 걸린다.

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)

 

< 문자열(StringOps를 통해서) >

: Predef 에 String을 StringOps로 바꾸는 암시적 변환이 있어 문자열을 시퀀스처럼 다룰 수 있다.

scala> def hasUpperCase(s: String) = s.exists(_.isUpper)
hasUpperCase: (s: String)Boolean

scala> hasUpperCase("Robert Frost")
res14: Boolean = true

 

17.2 집합과 맵

: 스칼라의 Set과 Map은 변경 불가능한 객체와 변경 가능한 객체 존재

: 모든 스칼라의 소스 파일에 자동으로 임포트하는 Predef 객체에 변경 불가능한 맵, 집합이 정의되어 있음 

object Predef {
  type Map[A, +B] = collection.immutable.Map[A, B]             //  Map 타입
  type Set[A] = collection.immutable.Set[A]
  val Map = collection.immutable.Map                         // Map 싱글톤 객체
  val Set = collection.immutable.Set
  // ...
}

 

< 집합의 사용 >

: 집합은 특정 객체를 최대 하나만 들어가도록 보장한다

 

< Map 의 사용 >

: 어떤 값과 집합의 각 원소 사이에 연관 관계를 만든다.

 

< 디폴트 집합과 맵 >

: scala.collection.mutable.Set() 팩토리 메소드는 내부적으로 해시 테이블을 사용하는 scala.collection.mutable.HashSet을 반환한다. scala.collection.mutable.Map() 팩토리 메소드도 ~.HashMap을 반환한다.

: 변경 불가능한 집합과 맵은 팩토리에 얼마나 많은 원소를 전달하느냐에 따라 달라진다.(원소가 5개 보다 적은 집합에 대해서는 성능을 극대화하기 위해 특정 크기의 집합만 담당하는 특별한 클래스를 사용한다.)

 

< 정렬된 집합과 맵 >

- 정해진 순서대로 원소를 반환하는 이터레이터를 제공하는 맵이나 집합을 사용할 때 SortedSet, SortedMap 트레이트 사용

- SortedSet과 SortedMap 구현이 TreeSet, TreeMap

- 순서를 커스텀하게 정할 때, 정렬된 집합이나 정렬된 맵의 키에 Ordered 트레이트를 혼합하거나 암시적으로 Ordered 트레이트로 변환 가능해야 한다. 

scala> import scala.collection.immutable.TreeSet
import scala.collection.immutable.TreeSet

scala> val ts = TreeSet(9, 3, 1, 8, 0, 2, 7, 4, 6, 5)
ts: scala.collection.immutable.TreeSet[Int] = TreeSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

 

scala> import scala.collection.immutable.TreeMap
import scala.collection.immutable.TreeMap

scala> var tm = TreeMap(3 -> 'x', 1 -> 'x', 4 -> 'x')
tm: scala.collection.immutable.TreeMap[Int,Char]
  = Map(1 -> x, 3 -> x, 4 -> x)

scala> tm += (2 -> 'x')

scala> tm
res30: scala.collection.immutable.TreeMap[Int,Char] = Map(1 -> x, 2 -> x, 3 -> x, 4 -> x)

 

17.3 변경 가능 컬렉션과 변경 불가능 컬렉션

: mutable, immutable 중 어떤 것을 선택해야 할지 모르겠다면, immutable로 시작하고 나중에 필요에 따라 바꾸는 게 좋다. ( immutable이 mutable 보다 프로그램 추론이 쉽다. )

 

: mutable을 먼저 사용한다면 코드가 복잡하고 추론하기 어려울 때 immutable 고려( 변경 가능 컬렉션의 복사본을 언제 만들어야 할지 고민되거나, 누가 갖고 있는지나 누구에게 포함되어 있는지 많이 생각해야 한다면 )

 

: 컬렉션에 저장할 원소의 수가 적을 경우 immutable이 mutable보다 더 작게 메모리를 사용 

Ex. 변경 가능한 빈 HashMap은 80바이트이고 원소를 하나 추가할 때마다 16바이트가 더 든다. 변경 불가능한 빈 맵은 싱글톤 객체 하나를 모든 참조가 공유하기 때문에 포인터 필드 하나만큼의 메모리만 필요하다.

 

: 변경 불가능한 맵/집합은 크기가 4일 때까지 단일 객체(Set1~Set4, Map1~Map4)를 사용한다. 단일 객체 크기는 16~40 바이트 정도로 변경 가능한 맵/집합보다 공간이 훨씬 작다. => 많은 컬렉션이 작은 크기라면 컬렉션을 변경 불가능하게 만드는 것이 공간을 절약하고 성능을 향상하는 중요한 선택

 

: 스칼라에서는 맵/집합에 데이터를 조금 저장하는 것을 추구, immutable은 값을 매번 복사 => 메모리를 많이 잡아먹음 => 별도 객체

 

" 변경 불가능한 맵 "

var capital 

    = Map("US" -> "Washington", "France" -> "Paris")
capital += ("Japan" -> "Tokyo")
println(capital("France")) 

" 변경 가능한 맵 "

import scala.collection.mutable.Map // only change needed!
var capital = Map("US" -> "Washington", "France" -> "Paris")
capital += ("Japan" -> "Tokyo")
println(capital("France")) 

 

17.4 컬렉션 초기화

: 일반적인 초기화 방법은 초기 원소를 컬렉션 동반 객체의 팩토리 메소드에 넘기는 것(apply 메소드 호출로 변환)

: 동반 객체의 팩토리 메소드에 타입 명시 가능

scala> import scala.collection.mutable
import scala.collection.mutable

scala> val stuff = mutable.Set(42)
stuff: scala.collection.mutable.Set[Int] = Set(42)

scala> stuff += "abracadabra"
:15: error: type mismatch;
   found   : java.lang.String("abracadabra")
   required: Int
         stuff += "abracadabra"
                  ^

scala> val stuff = mutable.Set[Any](42)             // 타입 명시
stuff: scala.collection.mutable.Set[Any] = Set(42)

: 어떤 컬렉션을 다른 컬렉션으로 초기화하는 경우

scala> val colors = List("blue", "yellow", "red", "green")
colors: List[java.lang.String]
  = List(blue, yellow, red, green)


scala> import scala.collection.immutable.TreeSet
import scala.collection.immutable.TreeSet

scala> val treeSet = TreeSet(colors)                 
:15: error: could not find implicit value for 
parameter ord: Ordering[List[java.lang.String]]
      val treeSet = TreeSet(colors)                 
                              ^

scala> val treeSet = TreeSet[String]() ++ colors
treeSet: scala.collection.immutable.TreeSet[String]
     = TreeSet(blue, green, red, yellow)

 

< 배열이나 리스트로 바꾸기 >

: 배열이나 리스트로 바꿀 때 toList, toArray를 호출하면 된다.

: toList, toArray를 호출했을 때 각 원소의 순서는 호출하는 컬렉션이 생성한 이터레이터가 돌려주는 원소의 순서와 같다.

scala> treeSet.toList
res50: List[String] = List(blue, green, red, yellow)

 

< 변경 가능한 집합(맵)과 변경 불가능 집합(맵) 사이의 변환 >

: empty 메소드를 사용해 새로운 타입의 컬렉션 생성 => "++" , "++=" 연산자를 사용해 원소 추가

scala> import scala.collection.mutable
import scala.collection.mutable

scala> treeSet
res52: scala.collection.immutable.TreeSet[String] = 
  TreeSet(blue, green, red, yellow)

scala> val mutaSet = mutable.Set.empty ++= treeSet
mutaSet: scala.collection.mutable.Set[String] = 
  Set(yellow, blue, red, green)

scala> val immutaSet = Set.empty ++ mutaSet
immutaSet: scala.collection.immutable.Set[String] = 
  Set(yellow, blue, red, green)

 

17.5 튜플

: 배열, 리스트와 달리 여러 타입의 원소를 넣을 수 있다. => 데이터만 저장하는 단순한 클래스를 정의해야 하는 번거로움을 덜 수 있다.

: 각기 다른 타입의 객체를 결합할 수 있기 때문에 Traversable을 상속 X

: 튜플 원소에 접근할 대는 _n 메소드 사용(인덱스가 1부터 시작)

scala> longest = ("quick", 1)

scala> longest._1      // 첫 번째 원소
res53: String = quick

scala> longest._2
res54: Int = 1

 

scala> val (word, idx) = longest      // 패턴 매치
word: String = quick
idx: Int = 1

scala> word
res55: String = quick


scala> val word, idx = longest    // '()'를 생략하면 다른 결과, 한 표현식에서 여러 개의 정의 제공
word: (String, Int) = (quick,1)
idx: (String, Int) = (quick,1)

: 튜플은 'A 하나와 B 하나' 수준을 넘지 않는 데이터를 한데 묶을 때 아주 유용 

: 결합에 어떤 의미가 있거나 결합에 어떤 메소드를 추가하기 원한다면 클래스를 생성하는 게 좋다.(Ex. 연,월,일 => Date 클래스)  => 코드 이해가 쉽고, 컴파일러가 제공하는 기능을 통해 오류를 쉽게 찾을 수 있다.

+ Recent posts