Parece que Vector
estava atrasado para a festa das coleções Scala, e todas as publicações influentes do blog já haviam saído.
Em Java ArrayList
é a coleção padrão - eu poderia usar, LinkedList
mas apenas quando eu pensei em um algoritmo e me preocupei o suficiente para otimizar. No Scala, devo usar Vector
como padrão Seq
ou tentar descobrir quando List
é realmente mais apropriado?
scala
vector
scala-collections
Duncan McGregor
fonte
fonte
List<String> l = new ArrayList<String>()
blogs Scala de gravação , para que você acreditasse que todo mundo usa a List para obter uma qualidade persistente da coleção - mas o Vector é de uso geral o suficiente para usá-lo no lugar da List?List
quando digitoSeq()
no REPL.IndexedSeq
.Seq
tem mais de três anos. No Scala 2.11.4 (e anterior), o tipo concreto padrão deSeq
éList
.Respostas:
Como regra geral, o padrão é usar
Vector
. É mais rápido queList
em quase tudo e mais eficiente em memória para sequências de tamanho maior que trivial. Consulte esta documentação do desempenho relativo do vetor em comparação com as outras coleções. Existem algumas desvantagens em seguirVector
. Especificamente:List
(embora não tanto quanto você imagina)Outra desvantagem antes do Scala 2.10 foi que o suporte à correspondência de padrões era melhor
List
, mas isso foi corrigido no 2.10 com generalização+:
e:+
extratores.Há também uma maneira mais abstrata e algébrica de abordar essa questão: que tipo de sequência você tem conceitualmente ? Além disso, o que você é fazendo conceitualmente com isso? Se eu vir uma função que retorna um
Option[A]
, sei que ela possui alguns buracos em seu domínio (e, portanto, é parcial). Podemos aplicar essa mesma lógica às coleções.Se eu tenho uma sequência do tipo
List[A]
, estou afirmando efetivamente duas coisas. Primeiro, meu algoritmo (e dados) é totalmente estruturado em pilha. Segundo, estou afirmando que as únicas coisas que farei com esta coleção são cheias, O (n) travessias. Esses dois realmente andam de mãos dadas. Por outro lado, se eu tiver algo do tipoVector[A]
, a única coisa que afirmo é que meus dados têm uma ordem bem definida e um comprimento finito. Assim, as afirmações são mais fracasVector
e isso leva a uma maior flexibilidade.fonte
case head +: tail
oucase tail :+ head
. Para combinar com o vazio, você pode fazercase Seq()
e assim por diante. Tudo que você precisa está lá na API, que é mais versátil do queList
'sList
é implementado com uma lista vinculada individualmente.Vector
é implementado algo como o JavaArrayList
.Bem, a
List
pode ser incrivelmente rápido se o algoritmo puder ser implementado apenas com::
,head
etail
. Eu tive uma lição objetiva disso muito recentemente, quando venci o Javasplit
gerando um emList
vez de umArray
, e não consegui vencê-lo com mais nada.No entanto,
List
tem um problema fundamental: não funciona com algoritmos paralelos. Não posso dividir umList
em vários segmentos ou concatená-lo novamente, de maneira eficiente.Existem outros tipos de coleções que conseguem lidar com o paralelismo muito melhor - e
Vector
é um deles.Vector
também possui ótima localidade - o queList
não ocorre - o que pode ser uma vantagem real para alguns algoritmos.Portanto, considerando todas as coisas,
Vector
é a melhor opção, a menos que você tenha considerações específicas que tornam uma das outras coleções preferíveis - por exemplo, você pode escolherStream
se deseja uma avaliação e cache preguiçosos (Iterator
é mais rápido, mas não faz cache), ouList
se o algoritmo é naturalmente implementado com as operações que mencionei.By the way, é preferível usar
Seq
ouIndexedSeq
a menos que você quer um pedaço específico de API (comoList
's::
), ou mesmoGenSeq
ouGenIndexedSeq
se o seu algoritmo pode ser executado em paralelo.fonte
Vector
é uma estrutura de dados imutável no Scala?Algumas das declarações aqui são confusas ou até erradas, especialmente a ideia de que é imutável. O vetor em Scala é semelhante a um ArrayList. List e Vector são estruturas de dados imutáveis e persistentes (isto é, "baratas para obter uma cópia modificada"). Não existe uma opção padrão razoável, como pode ser para estruturas de dados mutáveis, mas depende do que o seu algoritmo está fazendo. List é uma lista vinculada individualmente, enquanto Vector é um número inteiro de base 32, ou seja, é um tipo de árvore de pesquisa com nós de grau 32. Usando essa estrutura, o Vector pode fornecer operações mais comuns razoavelmente rápidas, ou seja, em O (log_32 ( n)). Isso funciona para pré-acrescentar, acrescentar, atualizar, acesso aleatório, decomposição na cabeça / cauda. A iteração em ordem seqüencial é linear. A lista, por outro lado, apenas fornece iteração linear e pré-tempo constante, decomposição na cabeça / cauda.
Pode parecer que o vetor substitui a lista em quase todos os casos, mas o prefixo, a decomposição e a iteração são frequentemente as operações cruciais nas seqüências de um programa funcional, e as constantes dessas operações são (muito) maiores para o vetor devido a à sua estrutura mais complicada. Fiz algumas medições, para que a iteração seja cerca de duas vezes mais rápida para a lista, o prefixo é cerca de 100 vezes mais rápido nas listas, a decomposição na cabeça / cauda é cerca de 10 vezes mais rápida nas listas e a geração de um traversable é cerca de 2 vezes mais rápida para vetores. (Provavelmente, porque o Vector pode alocar matrizes de 32 elementos ao mesmo tempo quando você o cria usando um construtor, em vez de acrescentar ou acrescentar elementos um por um).
Então, qual estrutura de dados devemos usar? Basicamente, existem quatro casos comuns:
fonte
Para coleções imutáveis, se você deseja uma sequência, sua principal decisão é usar um
IndexedSeq
ou aLinearSeq
, o que fornece garantias diferentes de desempenho. Um IndexedSeq fornece acesso aleatório rápido a elementos e uma operação rápida. Um LinearSeq fornece acesso rápido apenas ao primeiro elemento viahead
, mas também possui umatail
operação rápida . (Retirado da documentação Seq.)Para um,
IndexedSeq
você normalmente escolheria umVector
.Range
s eWrappedString
s também são IndexedSeqs.Para um,
LinearSeq
você normalmente escolheria umList
ou seu equivalente preguiçosoStream
. Outros exemplos sãoQueue
s eStack
s.Assim, em termos de Java,
ArrayList
usado da mesma forma que o ScalaVector
eLinkedList
da ScalaList
. Mas no Scala eu tenderia a usar a List com mais frequência do que Vector, porque o Scala tem um suporte muito melhor para funções que incluem a travessia da sequência, como mapeamento, dobragem, iteração etc. Você tenderá a usar essas funções para manipular a lista como um vetor. todo, em vez de acessar aleatoriamente elementos individuais.fonte
Vector
a iteração é mais rápida, mas alguém precisa compará-la para ter certeza.Vector
existem fisicamente juntos em RAM em grupos de 32, que se encaixam mais plenamente no cache da CPU ... por isso há menos perda de cacheEm situações que envolvem muito acesso aleatório e mutação aleatória, um
Vector
(ou - como dizem os médicos - aSeq
) parece ser um bom compromisso. Isso também é o que as características de desempenho sugerem.Além disso, a
Vector
classe parece funcionar bem em ambientes distribuídos sem muita duplicação de dados, porque não há necessidade de fazer uma cópia na gravação para o objeto completo. (Consulte: http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures )fonte
IndexedSeq
. O que também éVector
, mas isso é outra questão.IndexedSeq
que implementaSeq
.Seq(1, 2, 3)
é umLinearSeq
que é implementado usandoList
.Se você está programando imutável e precisa de acesso aleatório, o Seq é o caminho a seguir (a menos que você queira um conjunto, o que geralmente é necessário). Caso contrário, a Lista funciona bem, exceto que suas operações não podem ser paralelizadas.
Se você não precisar de estruturas de dados imutáveis, fique com o ArrayBuffer, pois é o equivalente do Scala ao ArrayList.
fonte