Scala'da Vector'i ne zaman seçmeliyim?


200

VectorScala koleksiyonları partisine geç kalmış gibi görünüyor ve tüm etkili blog gönderileri zaten terk edilmişti.

Java ArrayListvarsayılan koleksiyonu - kullanabilirsiniz LinkedListama sadece bir algoritma düşündüm ve optimize etmek için yeterince dikkatli. Scala'da Vectorvarsayılan olarak kullanmalı mıyım Seq, yoksa Listgerçekten daha uygun olduğunda çalışmaya mı çalışmalıyım ?


1
Sanırım burada ne demek istediğimi Java'da yazmak istiyorum List<String> l = new ArrayList<String>()Scala bloglar size herkesin kalıcı koleksiyon iyiliği elde etmek için List'i kullandığına inanmasını isterdim - ama Vector genel amaçlı onu List'in yerinde kullanmamız yeterli mi?
Duncan McGregor

9
@Debilski: Bununla ne demek istediğini merak ediyorum. REPL Listyazdığımda bir an olsun Seq().
missingfaktor

1
Hmm, şey, belgelerde öyle diyor. Belki bu sadece doğrudur IndexedSeq.
Debilski

1
Varsayılan beton tipine ilişkin yorum Seqüç yıldan daha eski. Scala 2.11.4 (ve öncesi) itibariyle varsayılan beton türü ' Seqdir List.
Mark Canlas

3
Rasgele erişim için vektör daha iyidir. Kafa, kuyruk erişimi için liste daha iyidir. Harita, filtre, vektör gibi toplu işlemler için, vektör 32 parça ile bir yığın olarak organize edildiğinden, liste öğeleri işaretçilerle birbirine organize ettiğinden, bu elemanların birbirine yakın olacağının garantisi yoktur.
johnsam

Yanıtlar:


280

Genel bir kural olarak, varsayılan olarak Vector. Daha hızlı daha var Listiçin neredeyse büyük-daha-önemsiz büyüklükteki dizileri için her şeyi bellek verimli ve daha fazlası. Diğer koleksiyonlara kıyasla Vector'in göreceli performansıyla ilgili bu belgelere bakın . Devam etmek için bazı dezavantajları var Vector. özellikle:

  • Kafadaki güncellemeler daha yavaştır List(düşündüğünüz kadar olmasa da)

Scala 2.10'dan önceki bir diğer dezavantaj, desen eşleştirme desteğinin daha iyi olmasıydı List, ancak bu 2.10'da genelleştirilmiş +:ve :+ekstraktörlerle düzeltildi .

Bu soruya yaklaşmanın daha soyut, cebirsel bir yolu da var: kavramsal olarak ne tür bir sıralamanız var? Ayrıca, kavramsal olarak bununla ne yapıyorsunuz? Bir döndüren bir işlev görürseniz, işlevin Option[A]kendi alanında bazı delikler olduğunu biliyorum (ve bu nedenle kısmi). Aynı mantığı koleksiyonlara da uygulayabiliriz.

Eğer bir dizi dizilim varsa List[A], etkili bir şekilde iki şey iddia ediyorum. İlk olarak, algoritmam (ve verilerim) tamamen yığın yapısına sahip. İkincisi, bu koleksiyonla yapacağım tek şeyin dolu olduğunu iddia ediyorum, O (n) çapraz geçişler. Bu ikisi gerçekten el ele gidiyor. Tersine, eğer bir şeyim varsa Vector[A], iddia ettiğim tek şey verilerimin iyi tanımlanmış bir sıraya ve sonlu bir uzunluğa sahip olmasıdır. Böylece, iddialar daha zayıftır Vectorve bu daha fazla esnekliğe yol açar.


2
2.10 bir süredir çıktı, Liste düzeni eşleşmesi hala Vector'dan daha mı iyi?
Tim Gautier

3
Liste düzeni eşleşmesi artık daha iyi değil. Aslında, tam tersi. Örneğin, baş ve kuyruk almak için case head +: tailveya yapabilirsiniz case tail :+ head. Boş karşı maç için, case Seq()vb. Yapabilirsiniz . İhtiyacınız olan her şey API'de var, bu da List's
Kai Sellgren

List, tek bağlantılı bir listeyle uygulanır. VectorJava gibi bir şey uygulanır ArrayList.
Josiah Yoder

6
@JosiahYoder ArrayList gibi bir şey uygulanmaz. ArrayList, dinamik olarak yeniden boyutlandırdığı bir diziyi sarar. Vektör, anahtarların değer dizini olduğu bir üçlüdür .
John Colanduoni

1
Özür dilerim. Detaylar hakkında belirsiz bir web kaynağına gidiyordum. Önceki beyanımı düzeltmeli miyim? Yoksa bu kötü form mu?
Josiah Yoder

93

Eh, bir Listalgoritma ile sadece uygulanabilir inanılmaz hızlı olabilir ::, headve tail. Son zamanlarda bunun yerine bir ders aldım, Java'yı splitbir Listyerine üreterek yendiğimde Arrayve başka bir şeyle yenemediğimde.

Ancak, Listtemel bir sorunu vardır: paralel algoritmalarla çalışmaz. Bir bölme değil Listbirden fazla parça halinde ya da etkili bir şekilde, geri bağlamak.

Paralelliği daha iyi ele alabilecek başka koleksiyonlar da vardır - ve Vectorbunlardan biridir. VectorAyrıca Listbazı algoritmalar için gerçek bir artı olabilir - ki - büyük yerellik vardır .

Yani, her şey göz önünde Vectoriyi seçimdir sürece sen çok tercih edilen başka koleksiyonlarından birini yapmak özel hususlar var -, seçtiğiniz olabilir örneğin Streamtembel değerlendirme ve önbelleğe alma isterseniz ( Iteratorhızlıdır ancak önbellek değil), ya da Listeğer algoritma bahsettiğim operasyonlarla doğal olarak uygulanır.

Arada, kullanılması tercih edilir Seqveya IndexedSeqAPI belirli bir parçasını (örneğin istemedikçe List'ler ::), hatta GenSeqveya GenIndexedSeqeğer algoritma paralel olarak çalıştırılabilir.


3
Cevap için teşekkürler. Ne demek "harika bir konuma sahip"?
Ngoc Dao

10
@ngocdaothanh Verilerin bellekte birbirine yakın bir şekilde gruplandırılması, verilerin ihtiyaç duyduğunuzda önbellekte olma olasılığını artırır.
Daniel C. Sobral

1
@ user247077 Evet, Listeler, bahsettiğim ayrıntılar göz önüne alındığında performansları Vektörler'i yenebilir. Ve vektörlerin tüm eylemleri O (1) amortismana tabi tutulmaz . Aslında, değişmez veri yapılarında (bu durumda), her iki uçtaki alternatif ekleme / silme işlemleri hiç bir zaman amortismana tabi tutulmayacaktır. Bu durumda, önbellek işe yaramaz çünkü her zaman vektörü kopyalarsınız.
Daniel

1
@ user247077 Belki de VectorScala'da değişmez bir veri yapısı olduğunun farkında değil misiniz ?
Daniel

1
@ user247077 Eklemeyi daha ucuz hale getirmek için dahili olarak değiştirilebilir bazı şeyler de dahil olmaktan çok daha karmaşık, ancak değişmez liste en iyi senaryosu olan bir yığın olarak kullandığınızda, yine de bağlantılı listenin aynı bellek özelliklerine sahip olursunuz, ancak çok daha büyük bir bellek ayırma profiline sahip.
Daniel C. Sobral

29

Buradaki ifadelerin bazıları kafa karıştırıcı ya da yanlış, özellikle değişmez olduğu fikri. Scala'daki vektör ArrayList gibi bir şey. List ve Vector değişmez, kalıcı (yani "değiştirilmiş bir kopya almak için ucuz") veri yapılarıdır. Değişebilir veri yapıları için makul bir varsayılan seçenek yoktur, ancak algoritmanızın ne yaptığına bağlıdır. Liste tek başına bağlantılı bir listedir, Vector ise bir taban-32 tam sayı trie iken, 32 derece düğümlü bir tür arama ağacıdır. Bu yapıyı kullanarak, Vector en yaygın işlemleri oldukça hızlı sağlayabilir, yani O (log_32 ( n)). Bu, baş / kuyrukta başa ekleme, ekleme, güncelleme, rasgele erişim, ayrışma için çalışır. Ardışık sırada yineleme doğrusaldır. Öte yandan liste sadece doğrusal yineleme ve sabit zaman başlangıcı, baş / kuyrukta ayrışma sağlar.

Bu, Vector neredeyse tüm durumlarda Liste için iyi bir yedek gibi görünebilir, ancak başa dönme, ayrışma ve yineleme genellikle işlevsel bir programdaki diziler üzerinde önemli işlemlerdir ve bu işlemlerin sabitleri vektör nedeniyle (daha) daha yüksektir. daha karmaşık yapısına. Birkaç ölçüm yaptım, bu yüzden yineleme liste için yaklaşık iki kat daha hızlı, başa çıkma listelerde yaklaşık 100 kat daha hızlı, baş / kuyrukta ayrışma listelerde yaklaşık 10 kat daha hızlı ve çaprazlanabilir bir üretimden vektörler için yaklaşık 2 kat daha hızlı. (Muhtemelen bunun nedeni, Vector öğelerini tek tek eklemek veya eklemek yerine bir oluşturucu kullanarak oluşturduğunuzda 32 öğenin dizilerini aynı anda ayırabilmesidir).

Peki hangi veri yapısını kullanmalıyız? Temel olarak, dört yaygın durum vardır:

  • Dizileri yalnızca harita, filtre, katlama vb. İşlemlerle dönüştürmemiz gerekir: temelde önemli değil, algoritmamızı genel olarak programlamalıyız ve hatta paralel dizileri kabul etmekten faydalanabiliriz. Sıralı işlemler için Liste muhtemelen biraz daha hızlıdır. Ancak optimizasyon yapmanız gerekiyorsa karşılaştırmalı değerlendirmeniz gerekir.
  • Çok sayıda rastgele erişime ve farklı güncellemelere ihtiyacımız var, bu yüzden vektör kullanmalıyız, liste yasak yavaş olacak.
  • Listeler üzerinde klasik işlevsel bir şekilde çalışır, bunları tekrarlayarak ve yinelemeli ayrışmayla yineleyerek oluştururuz: kullanım listesi, vektör 10-100 veya daha fazla faktör kadar yavaşlar.
  • Temelde zorunlu olan ve bir listede çok sayıda rasgele erişim gerçekleştiren, hızlı sıralama gibi bir şey olan, performans açısından kritik bir algoritmaya sahibiz: örneğin, ArrayBuffer gibi zorunlu bir veri yapısı kullanın ve verilerinizi yerel olarak ve ona kopyalayın.

24

Değişmez koleksiyonlar için, bir dizi istiyorsanız, ana kararınız performans için farklı garantiler veren bir IndexedSeqveya a kullanılıp kullanılmayacağıdır LinearSeq. Bir IndexedSeq, elemanlara hızlı rastgele erişim ve hızlı uzunluk operasyonu sağlar. Bir LinearSeq sadece ilk elemana üzerinden hızlı erişim sağlar head, fakat aynı zamanda hızlı bir tailişlem gerçekleştirir. (Seq belgelerinden alınmıştır.)

Bir için IndexedSeqnormalde bir Vector. Ranges ve WrappedStrings aynı zamanda IndexedSeqs'dir.

Bir için LinearSeqnormalde bir Listveya tembel eşdeğerini seçersiniz Stream. Diğer örnekler Queues ve Stacks'dir.

Yani, Java açısından ArrayListScala'nın gibi kullanılabilen Vectorve LinkedListbenzer Scala'nın için List. Ancak Scala'da List'i Vector'dan daha sık kullanma eğilimindeyim, çünkü Scala, eşleme, katlama, yineleme vb. tek tek elemanlara rastgele erişmektense.


Ancak Vector'in yinelemesi List'inkinden daha hızlıysa ve katlama vb.
Duncan McGregor

@Duncan, Vector'in yinelemesinin daha hızlı olduğunu nereden duydunuz? Başlangıç ​​için, bağlantılı bir listeyle ihtiyaç duymadığınız geçerli dizini izlemeniz ve güncellemeniz gerekir. Ben liste fonksiyonları "özel durumlar" demezdim - onlar fonksiyonel programlama ekmek ve tereyağı. Bunları kullanmamak Java'yı for- while while döngüleri olmadan programlamaya çalışmak gibidir.
Luigi Plinge

2
Eminim ki Vectoryineleme daha hızlıdır, ama emin olmak için birisinin bunu karşılaştırması gerekir.
Daniel Spiewak

(?) Bence öğeler Vectorfiziksel olarak daha tam işlemci önbelleği sığacak 32 kişilik gruplar halinde, RAM bir arada bulunmasına ... yani daha az önbellek bayan var
richizy

2

Çok rastgele erişim ve rastgele mutasyon içeren durumlarda, a Vector(veya - dokümanların dediği gibi - a Seq) iyi bir uzlaşma gibi görünmektedir. Bu aynı zamanda ne performans özellikleri göstermektedir.

Ayrıca, Vectorsınıf, dağıtılmış ortamlarda çok fazla veri çoğaltması olmadan güzel oynuyor gibi görünüyor çünkü tüm nesne için yazma üzerine kopyalamaya gerek yok. (Bkz. Http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures )


1
Öğrenmek için çok fazla ... Vector varsayılan Seq olmak ne anlama geliyor? Seq (1, 2, 3) yazarsam Vector [Int] değil List [Int] alırım.
Duncan McGregor

2
Rasgele erişiminiz varsa, bir IndexedSeq. Bu da Vector, ama bu başka bir mesele.
Daniel C. Sobral

@DuncanMcGregor: Vektör IndexedSequygulayan varsayılan değerdir Seq. Seq(1, 2, 3)a, LinearSeqkullanılarak uygulandığı List.
pathikrit

0

Değişmez bir şekilde programlıyorsanız ve rastgele erişime ihtiyacınız varsa, Seq gitmenin yoludur (genellikle yaptığınız bir Set istemiyorsanız). Aksi takdirde Liste iyi çalışır, ancak işlemleri paralelleştirilemez.

Değişmez veri yapılarına ihtiyacınız yoksa, ArrayList ile Scala eşdeğeri olduğundan ArrayBuffer'a sadık kalın.


Değişmez, kalıcı koleksiyonlar alanına bağlıyım. Demek istediğim, rastgele erişime ihtiyacım olmasa bile, Vector etkili bir şekilde Listeyi değiştirdi mi?
Duncan McGregor

2
Biraz kullanım durumuna bağlıdır. Vektörler daha dengelidir. Yineleme listeden daha hızlı ve rastgele erişim çok daha hızlı. Bir katlayıcıdan yapılabilen toplu bir güncelleştirme olmadığı sürece, yalnızca liste başına eklenmediğinden güncellemeler daha yavaştır. Bununla birlikte, çok yönlü olduğu için Vector en iyi varsayılan seçim olduğunu söyledi.
Joshua Hartman

Benim sorumun kalbine geldiğini düşünüyorum - Vektörler o kadar iyi ki, örnekleri genellikle Listenin gösterildiği yerlerde de kullanabiliriz.
Duncan McGregor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.