Scala에서 Vector를 언제 선택해야합니까?


200

그것은 Vector스칼라 컬렉션 파티에 늦었 던 것으로 보이며 모든 영향력있는 블로그 게시물은 이미 떠났습니다.

Java ArrayList에서는 기본 모음입니다 LinkedList. 알고리즘을 통해 생각하고 최적화하기에 충분히주의를 기울 였을 때만 사용할 수 있습니다 . 스칼라에서 Vector기본값 으로 사용 Seq하거나 List실제로 더 적절한 시기를 해결 해야합니까?


1
내가 여기서 의미하는 바는 Java에서 쓰기 List<String> l = new ArrayList<String>()Scala 블로그를 작성 한다는 것입니다. 모두가 지속적인 수집의 이점을 얻기 위해 모두 List를 사용한다고 생각하지만 List의 장소에서 사용해야 할 정도로 벡터 범용입니까?
Duncan McGregor

9
@ Debilski : 그게 무슨 뜻인지 궁금합니다. 내가 얻을 List내가 입력하면 Seq()REPL에서.
missingfaktor

1
흠, 그것은 문서에서 그렇게 말합니다. 아마도 이것은 사실 일뿐입니다 IndexedSeq.
Debilski

1
기본 콘크리트 유형에 대한 의견은 Seq3 년이 넘었습니다. 스칼라 2.11.4 (이전)에서 기본 콘크리트 유형은 Seq입니다 List.
Mark Canlas 5

3
랜덤 액세스의 경우 벡터가 더 좋습니다. 머리, 꼬리 접근, 목록이 더 좋습니다. 맵, 필터, 벡터와 같은 대량 작업의 경우 벡터가 청크로 32 개의 요소로 구성되는 반면 목록은 서로 포인터로 요소를 구성하므로 이러한 요소가 서로 가깝다는 보장은 없습니다.
johnsam

답변:


280

일반적으로 기본값은을 사용 Vector합니다. 그것은보다 더 빨리이다 List에 대한 거의 보다 큰 사소한 크기의 시퀀스에 대한 모든 메모리 효율적이고. 다른 컬렉션과 비교하여 Vector의 상대적 성능에 대한 이 설명서 를 참조하십시오 . 와 함께 몇 가지 단점이 있습니다 Vector. 구체적으로 특별히:

  • 헤드의 업데이트 속도가 느리다 List(생각할 수는 없지만)

Scala 2.10 이전의 또 다른 단점은 패턴 일치 지원이 더 List좋았지 만 일반화 +::+추출기를 사용 하여 2.10에서 수정되었습니다 .

이 질문에 접근하는 더 추상적이고 대수적인 방법이 있습니다. 개념적으로 어떤 종류의 순서 가 있습니까? 또한 개념적으로 무엇 을하고 있습니까? 를 반환하는 함수를 보면 Option[A]해당 함수에 도메인에 약간의 구멍이 있음을 알 수 있습니다 (따라서 부분적 임). 이 같은 논리를 컬렉션에 적용 할 수 있습니다.

type의 시퀀스가 ​​있으면 List[A]효과적으로 두 가지를 주장합니다. 첫째, 내 알고리즘 (및 데이터)은 완전히 스택 구조입니다. 두 번째로, 나는이 컬렉션과 함께 할 유일한 것은 O (n) 순회라고 주장합니다. 이 두 사람은 실제로 함께합니다. 반대로, type의 무언가가있는 경우 Vector[A], 내가 주장 하는 유일한 것은 데이터가 잘 정의 된 순서와 유한 길이를 가지고 있다는 것입니다. 따라서 어설 션은로 약해지고 Vector유연성이 향상됩니다.


2
2.10은 얼마 동안 사용되지 않았습니다. 목록 패턴 일치가 여전히 벡터보다 더 좋습니까?
Tim Gautier

3
목록 패턴 일치가 더 이상 좋지 않습니다. 사실, 그것은 상당히 반대입니다. 예를 들어 머리와 꼬리를 얻으려면 case head +: tail또는을 할 수 있습니다 case tail :+ head. 공란에 맞서기 위해 case Seq()그렇게 할 수 있습니다 . 당신이 필요로하는 모든 것이 더 융통성보다, 이는 API에서 존재 List
카이 Sellgren

List단일 연결 목록으로 구현됩니다. VectorJava와 같은 것으로 구현 ArrayList됩니다.
Josiah Yoder

6
@JosiahYoder ArrayList와 같이 구현되지 않았습니다. ArrayList는 동적으로 크기가 조정되는 배열을 래핑합니다. Vector는 키이며 값의 인덱스 인 trie 입니다.
존 콜 랜두 오니

1
죄송합니다. 세부 사항에 대해 모호한 웹 소스를 사용하고있었습니다. 이전 진술을 수정해야합니까? 아니면 나쁜 형태입니까?
Josiah Yoder

93

음,이 List알고리즘은 전적으로 구현 될 수있는 경우에 매우 빠르게 할 수있다 ::, head하고 tail. 나는 최근 splitList대신을 생성하여 Java를 이길 때 객체에 대한 교훈을 얻었고 다른 것으로 그것을 이길 Array수 없었습니다.

그러나 List근본적인 문제가 있습니다. 병렬 알고리즘에서는 작동하지 않습니다. List효율적인 방식으로 여러 세그먼트로 분할 하거나 다시 연결할 수 없습니다 .

병렬 처리를 훨씬 더 잘 처리 할 수있는 다른 종류의 컬렉션이 있으며 그 Vector중 하나입니다. Vector또한 지역에 따라 다릅니다- List그렇지 않습니다-일부 알고리즘에는 실제로 도움이 될 수 있습니다.

그래서, 모든 것을 고려, Vector최선의 선택을 하지 않는 한 당신은 바람직 다른 컬렉션 중 하나 만들어 특정 고려 사항이 -, 당신이 선택할 수 있습니다 예를 들어 Stream당신이 게으른 평가 및 캐싱을 원하는 경우는 ( Iterator빠른하지만 캐시하지 않습니다), 또는 List경우 알고리즘은 언급 한 작업으로 자연스럽게 구현됩니다.

그런데, 그것을 사용하는 것이 바람직하다 Seq또는 IndexedSeq당신이 API의 특정 조각을 (예 : 원하지 않는다면 List::), 또는 GenSeq또는 GenIndexedSeq경우 알고리즘은 병렬로 실행할 수 있습니다.


3
답변 해주셔서 감사합니다. "지역성이 크다"는 무슨 뜻입니까?
Ngoc Dao

10
@ngocdaothanh 데이터가 메모리에 밀접하게 그룹화되어 필요할 때 데이터가 캐시에있을 가능성을 높입니다.
Daniel C. Sobral

1
@ user247077 예, 언급 한 특정 사항을 고려할 때 목록에서 성능이 벡터를 이길 수 있습니다. 그리고 벡터의 모든 행동이 O (1)로 상각되는 것은 아닙니다 . 실제로, 불변 데이터 구조 (이 경우)에서 양쪽 끝에있는 대체 삽입 / 삭제는 전혀 상각되지 않습니다. 이 경우 항상 벡터를 복사하기 때문에 캐시는 쓸모가 없습니다.
Daniel C. Sobral

1
@ user247077 아마도 Vector스칼라의 불변 데이터 구조 라는 것을 모르 십니까?
Daniel C. Sobral

1
@ user247077 더 저렴하게 추가 할 수 있도록 내부적으로 변경 가능한 것들을 포함하여 그보다 더 복잡하지만 변경 불가능한 목록 최적 시나리오 인 스택으로 사용할 때 여전히 연결된 목록과 동일한 메모리 특성을 갖습니다. 훨씬 더 큰 메모리 할당 프로파일.
Daniel C. Sobral

29

여기의 문장 중 일부는 혼란 스럽거나 잘못되었습니다. 특히 스칼라의 불변. 벡터는 ArrayList와 같습니다. 리스트와 벡터는 모두 불변적이고 영구적이다 (즉, "수정 된 사본을 얻기 위해 저렴한"데이터 구조). 변경 가능한 데이터 구조에 대한 적절한 기본 선택은 없지만 알고리즘이 수행하는 작업에 따라 다릅니다. List는 단독으로 연결된 목록이며 Vector는 base-32 integer trie입니다. 즉, 등급이 32 인 노드를 가진 일종의 검색 트리입니다.이 구조를 사용하면 Vector는 가장 일반적인 작업을 합리적으로 빠르게 제공 할 수 있습니다 (예 : O (log_32 ( 엔)). 그것은 머리 / 꼬리에 추가, 추가, 업데이트, 임의 액세스, 분해에 작동합니다. 순차적 순서의 반복은 선형입니다. 반면에리스트는 선형 반복과 일정한 시간 프리 펜드, 헤드 / 테일의 분해를 제공합니다.

이것은 거의 모든 경우에 Vector가 List를 대체하는 것처럼 보일 수 있지만, prepend, decomposition 및 iteration은 종종 함수형 프로그램의 시퀀스에서 중요한 작업이며 이러한 연산의 상수는 벡터의 경우 훨씬 높습니다. 더 복잡한 구조로 몇 가지 측정을 수행했기 때문에 반복이 목록보다 약 두 배 빠르고, 선행은 목록에서 약 100 배 빠르며, 머리 / 꼬리의 분해는 목록에서 약 10 배 빠르며 트래 버블 가능한 생성은 벡터의 경우 약 2 배 빠릅니다. (이것은 아마도 요소를 하나씩 추가하거나 추가하는 대신 빌더를 사용하여 빌드 할 때 Vector가 32 요소의 배열을 한 번에 할당 할 수 있기 때문일 수 있습니다).

어떤 데이터 구조를 사용해야합니까? 기본적으로 네 가지 일반적인 경우가 있습니다.

  • map, filter, fold 등과 같은 연산으로 만 시퀀스를 변환하면됩니다. 기본적으로 중요하지 않습니다. 알고리즘을 일반적으로 프로그래밍해야하며 병렬 시퀀스를 받아들이면 도움이 될 수도 있습니다. 순차적 작업의 경우 목록이 약간 더 빠릅니다. 그러나 최적화해야 할 경우 벤치마킹해야합니다.
  • 우리는 많은 랜덤 액세스와 다른 업데이트가 필요하므로 벡터를 사용해야합니다.리스트는 엄청나게 느릴 것입니다.
  • 우리는 고전적인 기능적인 방식으로 목록을 다루며 재귀 적 분해에 의해 선행하고 반복하여 목록을 만듭니다. 사용 목록, 벡터는 10-100 배 이상 느려질 것입니다.
  • 우리는 기본적으로 필수적이며 목록에서 많은 무작위 액세스를 수행하는 성능 결정 알고리즘을 가지고 있습니다. 빠른 정렬과 같은 : 명령형 데이터 구조 (예 : ArrayBuffer)를 사용하고 로컬에서 데이터를 복사하십시오.

24

당신이 순서를 원하는 경우 불변의 컬렉션을 들어, 당신의 주요 의사 결정은 사용 여부 IndexedSeq또는 LinearSeq성능에 대해 서로 다른 보증을 제공하는이. IndexedSeq는 요소에 대한 빠른 임의 액세스 및 빠른 길이 작업을 제공합니다. LinearSeq는를 통해 첫 번째 요소에만 빠르게 액세스 할 수 head있지만 빠른 tail작동도 제공합니다. (Seq 문서에서 가져온 것입니다.)

의 경우 IndexedSeq일반적으로을 선택합니다 Vector. Ranges 및 WrappedStrings도 IndexedSeq입니다.

a를 위해 LinearSeq당신은 일반적으로 List또는 그것의 게으른 동등 물을 선택할 것 Stream입니다. 다른 예는 Queues 및 Stacks입니다.

자바 용어 ArrayList로 스칼라 VectorLinkedList비슷하게 , 스칼라 와 유사하게 사용됩니다 List. 그러나 Scala에서는 Vector보다 List를 더 자주 사용하는 경향이 있습니다. Scala는 매핑, 접기, 반복 등과 같은 시퀀스 순회를 포함하는 함수를 훨씬 더 잘 지원하기 때문입니다. 이러한 함수를 사용하여 목록을 개별 요소에 무작위로 액세스하지 않고 전체.


그러나 Vector의 반복이 List보다 빠르며 접기 등을 매핑 할 수 있다면 일부 특수한 경우 (실제로는 List에 특화된 모든 FP 알고리즘)를 제외하고 List는 본질적으로 레거시 인 것처럼 보입니다.
Duncan McGregor

@ Duncan Vector의 반복이 빠르다는 것을 어디에서 들었습니까? 시작하려면 현재 목록을 추적하고 업데이트해야합니다.이 목록은 링크 된 목록과 함께 필요하지 않습니다. 나는리스트 함수를 "특수 사례"라고 부르지 않을 것이다. 그것들은 함수형 프로그래밍의 빵과 버터이다. 그것들을 사용하지 않는 것은 for- 또는 while-loop없이 Java를 프로그래밍하는 것과 같습니다.
Luigi Plinge

2
Vector반복 더 빠르다고 확신 하지만 누군가 벤치마킹해야합니다.
Daniel Spiewak

Vector32 개의 그룹으로 RAM 에 물리적으로 (?) 요소 가 함께 존재 한다고 생각 합니다. CPU 캐시에 더 잘 맞습니다. 따라서 캐시 미스가 줄어 듭니다
richizy

2

많은 무작위 접근과 무작위 돌연변이가 관련된 상황에서 a Vector(또는 문서에서 말했듯이 a Seq)는 좋은 타협으로 보입니다. 이것은 또한 성능 특성이 제안하는 것입니다.

또한 Vector전체 객체에 대해 기록 중 복사를 수행 할 필요가 없으므로 많은 데이터 복제가없는 분산 환경에서 클래스가 훌륭하게 재생되는 것처럼 보입니다. ( http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures 참조 )


1
배워야 할 것이 많습니다 ... Vector가 기본 Seq가되는 것은 무엇을 의미합니까? Seq (1, 2, 3)을 쓰면 Vector [Int]가 아닌 List [Int]가됩니다.
Duncan McGregor

2
임의 액세스 권한이있는 경우을 사용하십시오 IndexedSeq. 또한 어느 Vector것이지만 그것은 또 다른 문제입니다.
Daniel C. Sobral

@DuncanMcGregor : 벡터가 기본값 IndexedSeq으로 구현 Seq됩니다. Seq(1, 2, 3)A는 LinearSeq사용하여 구현된다 List.
pathikrit

0

프로그래밍이 불필요하고 임의 액세스가 필요한 경우 Seq를 사용하는 방법입니다 (실제로 실제로 수행하는 Set을 원하지 않는 한). 그렇지 않으면 List는 작업을 병렬화 할 수 없다는 점을 제외하고는 잘 작동합니다.

불변의 데이터 구조가 필요하지 않은 경우 ArrayBuffer는 ArrayList와 동일한 스칼라이므로 ArrayBuffer를 사용하십시오.


나는 불변의 영구 컬렉션의 영역을 고수하고 있습니다. 내 요점은 무작위 액세스가 필요하지 않더라도 Vector가 효과적으로 List를 대체 했습니까?
Duncan McGregor

2
사용 사례에 따라 조금씩 다릅니다. 벡터가 더 균형을 이룹니다. 반복이 목록보다 빠르며 임의 액세스가 훨씬 빠릅니다. 빌더로 수행 할 수있는 접기의 대량 업데이트가 아닌 한 목록 앞에 붙지 않기 때문에 업데이트 속도가 느려집니다. 즉, Vector는 매우 다양하기 때문에 최선의 기본 선택이라고 생각합니다.
Joshua Hartman

내 질문의 중심에 도달한다고 생각합니다. 벡터는 너무 좋아서 예제가 일반적으로 List를 표시하는 곳에서도 사용할 수 있습니다.
Duncan McGregor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.