[Scala] 컬렉션

smlee·2023년 8월 18일
0

Scala

목록 보기
22/37
post-thumbnail

이 글은 Programming in Scala 4/e Chapter.17을 읽고 정리한 글입니다.


이전 챕터에는 리스트만 알아보았지만, 스칼라에서는 풍부한 컬렉션 라이브러리를 제공한다. 이 챕터에서는 그 컬렉션들에 대해 공부해볼 예정이다.

컬렉션을 초기화하고 생성하는 일반적인 방법은 초기 원소를 컬렉션 동반 객체의 팩토리 메서드에 넘기는 것이다. 동반 객체 이름 뒤에 있는 괄호 안에 모든 원소를 쓰기만 하면 된다.

시퀀스 sequence

시퀀스 타입은 순서가 정해진 데이터 그룹을 가지고 작업할 수 있게 한다. 원소에 순서가 있으므로 index로 random access가 가능하다. 이러한 시퀀스 타입에는 앞쪽에서 알아본 리스트, 배열, 리스트 버퍼, 배열 버퍼, StringOps들이 있다.
리스트는 앞 챕터에서 알아보았으니 생략할 예정이다.

1. 배열 Array

배열은 원소의 시퀀스를 저장하며, 임의의 위치에 있는 원소에 효율적으로 접근하게 해준다.

val fiveArray = new Array[Int](5)

위와 같이 초기값이 없다면 new 키워드와 소괄호 안에 배열의 길이를 선언해주면 된다.

그러면 위와 같이 초기값 0이 들어간 채로 길이가 5인 배열이 선언된 것을 알 수 있다.
반면 초기화 값을 알 때는 직접 값을 넣어주면 된다.

val numArray = Array(1,2,3,4,5)

위와 같이 초기값을 안다면 new 키워드를 사용하지 않고 소괄호에 바로 값들을 넣어주면 된다.

2. 리스트 버퍼

List 클래스는 리스트의 앞쪽에 대해서는 빠른 접근을 제공하지만, 끝쪽에 대해서는 제공하지 않는다. 따라서 리스트 끝부분에 원소를 추가하려면 앞에 원소를 추가하고 reverse를 사용하는데, 이렇게 추가적인 reverse 연산을 사용하지 않아도 되는 방법은 ListBuffer를 사용하는 방법이다.

import scala.collection.mutable.ListBuffer

val buf = new ListBuffer[Int]

위와 같이 scala.collection.mutable.ListBuffer를 사용하여 선언할 수 있다.
appendprepend라는 메서드를 제공한다. 이 메서드들은 상수 시간 내에 원소를 추가할 수 있다.
또한 +=을 사용하면 리스트 뒤쪽에 원소를 추가할 수 있으며, +=:를 사용하면 리스트 앞에 원소를 추가할 수 있다. 다른 원소들처럼 toList를 사용하여 리스트로 변환 가능하다.

이러한 ListBuffer를 사용하면 스택 오버플로를 피할 수 있다.

3. 배열 버퍼 Array Buffer

배열 버퍼는 끝부분시작 부분에 원소를 추가하거나 삭제할 수 있다는 점을 제외하면 배열과 같다.
새 원소를 추가하거나 삭제하는 데는 평균적으로 상수 시간이 걸리지만, 때때로 버퍼의 내용을 저장하기 위해 새로운 배열을 할당해야 하므로 선형 시간이 걸릴 때도 있다.

import scala.collection.mutable.ArrayBuffer

val buf = new ArrayBuffer[Int]()

ArrayBuffer는 ListBuffer와 같은 방법으로 선언한다. ArrayBuffer 역시 +=를 통해 끝부분에 새 원소를 추가할 수 있다.
length 메서드를 통해 ArrayBuffer의 길이를 가져올 수 있으며, 인덱스로 random access가 가능하다.

4. StringOps

우리는 import를 공부한 챕터에서 스칼라가 모든 코드에 대하여 Predef를 무조건 import시킨다는 것을 공부했었다. 이러한 Predef에 String을 StringOps로 바꾸는 암시적 변환이 있기 때문에, 시퀀스처럼 문자열을 다룰 수 있다.

위의 예시에서 hasUpperCase에서 exists 메서드를 호출한다. 하지만, String에는 exists라는 메서드가 없다. 이는 StringOps에 있는 메서드를 암시적으로 변환할 수 있기 때문이다.

집합과 맵

Scala에서는 집합을 나타내는 SetMap을 만들면 디폴트로 immutable 객체가 생긴다. 만약 변경가능한 객체를 원한다면 명시적으로 import해야 한다. 이는 스칼라가 변경 불가능한 객체를 더 권장하기 때문이다.

1. 집합의 사용

집합의 핵심 특징은 특정 객체는 최대 하나만 들어가도록 보장한다는 적이다. 즉, 중복을 허용하지 않으면서 값들을 저장하는 것이다. 이때, 어떤 객체가 같은지는 ==로 결정한다.

val numSet = Set(1,2,3)

과 같은 형식으로 선언할 수 있다.

집합 연산

형태설명
set명 + 추가할 원소 [ex. numSet + 5]변경 불가능한 집합에 원소를 추가한다.[numSet에 원소 5를 추가한다.]
set명 - 삭제할 원소 [ex. numSet - 3]변경 불가능한 집합에서 원소를 제거한다. [numSet에서 원소 3을 삭제한다.]
set명 ++ 리스트 [ex. numSet ++ List(5, 6)]변경 불가능한 집합에서 여러 원소를 추가한다. [numSet에서 원소 5, 6을 추가]
set명 -- 리스트 [ex. numSet -- List(1,2) ]변경 불가능한 집합에서 여러 원소를 제거한다. [numSet에서 원소 1,2를 제거]
set명 & 다른 set명 [ex. numSet & anotherSet]두 세트에서 교집합을 구한다.
size집합의 크기를 구한다.
contains(원소)해당 원소가 세트에 존재하는지 여부를 확인한다. -> boolean형 리턴
clear모든 원소를 삭제한다.

2. 맵의 사용

맵은 어떤 값과 집합의 각 원소 사이에 연관 관계를 만든다. 맵을 사용하면 key값을 통해 접근할 수 있다. key-value로 값들을 저장한다.

val map = mutable.Map.empty[String,Int]

와 같은 형태로 선언한다. Map 역시 immutable이 디폴트이므로 mutable을 사용하려면 위와 같이 별도로 패키지 명을 선언해야 한다. key->value 형태로 저장된다.

위와 같이 ->를 사용하여 연관관계를 지정하고 저장할 수 있다.

맵 연산

형태설명
맵명 + 저장할 쌍 [ex. map + ("a" -> 1)]변경 불가능한 맵에 원소(연관관계)를 추가한다.
맵명 - 삭제할 쌍 [ex. map - "a"]변경 불가능한 맵에 연관 관계를 삭제한다.
맵명 ++ 리스트 [ex. map ++ List("b"->5, "c"->6)]변경 불가능한 맵에 여러 원소를 추가한다.
맵명 -- 리스트 [ex. map -- List("a", "b", "c")]변경 불가능한 맵에 여러 원소들을 삭제한다.
sizemap에 들어있는 원소 수를 반환한다.
keys모든 키를 반환(이때, Iterable 객체를 반환한다.)
keySet모든 키를 집합으로 반환
values모든 값들을 반환한다.
isEmpty비어있는지 여부를 반환한다.

정렬된 집합과 맵

가끔씩 정해진 순서대로 원소를 반환하는 이터레이터를 제공하는 맵이나 집합이 필요할 때도 이다. SortedSetSortedMap 트레이트를 사용하면 된다. 이 두 트레이트의 구현은 각각 TreeMapTreeSet 클래스이다. 이때, 순서는 Ordering 트레이트를 따라 결정한다.

📚 Reference

  • Programming in Scala 4/e - Chapter 17

0개의 댓글