เมื่อใดที่ฉันควรเลือก Vector ใน Scala


200

ดูเหมือนว่าVectorจะมาช้าไปงานปาร์ตี้คอลเล็กชั่น Scala และโพสต์บล็อกที่มีอิทธิพลทั้งหมดได้ออกไปแล้ว

ใน Java ArrayListเป็นคอลเลกชันเริ่มต้น - ฉันอาจใช้LinkedListแต่เมื่อฉันคิดว่าผ่านอัลกอริทึมและดูแลเพียงพอที่จะปรับให้เหมาะสม ใน Scala ฉันควรจะใช้Vectorเป็นค่าเริ่มต้นของฉันSeqหรือพยายามหาเวลาListที่เหมาะสมกว่าจริงหรือไม่


1
ฉันเดาว่าฉันหมายถึงอะไรที่นี่ใน Java ฉันจะสร้างการเขียนList<String> l = new ArrayList<String>()บล็อกสกาล่าจะทำให้คุณเชื่อว่าทุกคนใช้รายการเพื่อรับความดีในการเก็บถาวร - แต่เวกเตอร์มีจุดประสงค์ทั่วไปเพียงพอที่เราควรใช้ในรายการ
Duncan McGregor

9
@Debilski: ฉันสงสัยว่าคุณหมายถึงอะไร ฉันได้รับListเมื่อพิมพ์Seq()ที่ REPL
missingfaktor

1
อืมมันก็พูดเช่นนั้นในเอกสาร IndexedSeqอาจจะเป็นเพียงความจริงสำหรับ
Debilski

1
ความคิดเห็นเกี่ยวกับประเภทเริ่มต้นที่เป็นรูปธรรมของSeqอายุเกินสามปี ในฐานะของ Scala 2.11.4 (และก่อนหน้า) เริ่มต้นที่เป็นรูปธรรมของประเภทคือSeq List
Mark Canlas

3
สำหรับการเข้าถึงแบบสุ่มเวกเตอร์จะดีกว่า สำหรับการเข้าถึงส่วนหัวส่วนท้ายจะดีกว่า สำหรับการดำเนินการจำนวนมากเช่นแผนที่ตัวกรองและเวกเตอร์เป็นที่ต้องการเนื่องจากเวกเตอร์ถูกจัดระเบียบด้วยองค์ประกอบ 32 ชิ้นในขณะที่รายการจัดเรียงองค์ประกอบที่มีตัวชี้ซึ่งกันและกันไม่มีการรับประกันว่าองค์ประกอบเหล่านี้อยู่ใกล้กัน
johnsam

คำตอบ:


280

Vectorในฐานะที่เป็นกฎทั่วไปเริ่มต้นที่จะใช้ มันเร็วกว่าListสำหรับเกือบทุกอย่างและหน่วยความจำมีประสิทธิภาพมากขึ้นสำหรับลำดับที่มีขนาดใหญ่กว่าเล็กน้อย ดูเอกสารนี้ของประสิทธิภาพเชิงสัมพัทธ์ของ Vector เมื่อเปรียบเทียบกับคอลเลกชันอื่น ๆ Vectorมีข้อเสียบางอย่างที่จะไปกับ โดยเฉพาะ:

  • การอัปเดตที่หัวจะช้ากว่าList(แม้ว่าจะไม่มากเท่าที่คุณคิด)

ข้อเสียอีกอย่างหนึ่งก่อนที่จะสกาล่า 2.10 คือการสนับสนุนการจับคู่รูปแบบที่ดีกว่าสำหรับListแต่นี่คือการแก้ไขใน 2.10 กับ generalized +:และ:+สกัด

นอกจากนี้ยังมีวิธีที่เป็นนามธรรมและเชิงพีชคณิตมากขึ้นในการเข้าใกล้คำถามนี้: สิ่งที่คุณเรียงลำดับแนวความคิดมี? นอกจากนี้คุณกำลังทำอะไรกับแนวคิด ? ถ้าฉันเห็นฟังก์ชั่นที่คืนค่ากลับOption[A]มาฉันรู้ว่าฟังก์ชันนั้นมีรูบางส่วนในโดเมนของมัน (และเป็นบางส่วน) เราสามารถใช้ตรรกะเดียวกันนี้กับคอลเลกชัน

หากฉันมีลำดับประเภทList[A]ฉันจะยืนยันสองสิ่งอย่างมีประสิทธิภาพ ก่อนอื่นอัลกอริทึมของฉัน (และข้อมูล) มีโครงสร้างแบบกองซ้อนทั้งหมด ประการที่สองฉันยืนยันว่าสิ่งเดียวที่ฉันจะทำกับคอลเล็กชั่นนี้เต็มรูปแบบ O (n) traversals สองคนนี้ไปด้วยกันจริง ๆ ตรงกันข้ามถ้าฉันมีบางสิ่งบางอย่างชนิดVector[A]ที่เพียง แต่สิ่งที่ฉันกำลังเข้าไปยุ่งคือว่าข้อมูลของฉันมีคำสั่งที่กำหนดไว้อย่างดีและมีความยาว จำกัด ดังนั้นการยืนยันนั้นอ่อนแอลงVectorและสิ่งนี้นำไปสู่ความยืดหยุ่นที่มากขึ้น


2
2.10 เปิดตัวมาระยะหนึ่งแล้วการจับคู่รูปแบบรายการยังดีกว่า Vector หรือไม่
Tim Gautier

3
การจับคู่รูปแบบรายการไม่ดีกว่าอีกต่อไป ในความเป็นจริงมันค่อนข้างตรงกันข้าม ยกตัวอย่างเช่นการที่จะได้รับหัวและหางหนึ่งสามารถทำหรือcase head +: tail case tail :+ headเพื่อให้ตรงกับความว่างเปล่าคุณสามารถทำได้case Seq()และอื่น ๆ ทุกสิ่งที่คุณต้องการมีอยู่ใน API ซึ่งมีความหลากหลายมากกว่าList's
Kai Sellgren

Listถูกนำไปใช้กับรายการที่เชื่อมโยงโดยลำพัง Vectorจะดำเนินการบางอย่างเช่นของ ArrayListJava
Josiah Yoder

6
@JosiahYoder มันนำไปใช้ไม่เหมือน ArrayList ArrayList ล้อมรอบอาร์เรย์ซึ่งปรับขนาดแบบไดนามิก Vector คือtrieโดยที่ keys คือดัชนีของค่า
John Colanduoni

1
ฉันขอโทษ. ฉันกำลังทำเว็บซอร์สซึ่งคลุมเครือเกี่ยวกับรายละเอียด ฉันควรแก้ไขข้อความก่อนหน้าของฉันหรือไม่ หรือว่าเป็นรูปแบบที่ไม่ดี?
Josiah Yoder

93

ดี, Listสามารถเหลือเชื่ออย่างรวดเร็วถ้าอัลกอริทึมสามารถดำเนินการได้เพียงลำพังกับ::, และhead tailฉันมีบทเรียนเชิงวัตถุเมื่อเร็ว ๆ นี้เมื่อฉันเอาชนะ Java splitด้วยการสร้างListแทนที่จะเป็นArrayและไม่สามารถเอาชนะสิ่งนั้นได้ด้วยสิ่งอื่น

อย่างไรก็ตามListมีปัญหาพื้นฐาน: มันไม่ทำงานกับอัลกอริธึมแบบขนาน ฉันไม่สามารถแบ่ง a ออกเป็นListหลายเซ็กเมนต์หรือเชื่อมต่อกลับเข้าด้วยกันได้อย่างมีประสิทธิภาพ

มีคอลเล็กชั่นประเภทอื่นที่สามารถจัดการความเท่าเทียมได้ดีกว่า - และVectorเป็นหนึ่งในนั้น Vectorนอกจากนี้ยังมีสถานที่ที่ดีเยี่ยม - ซึ่งListไม่ได้ - ซึ่งอาจเป็นข้อดีสำหรับอัลกอริทึมบางอย่าง

ดังนั้นทุกสิ่งที่ถือว่าVectorเป็นตัวเลือกที่ดีที่สุดเว้นแต่คุณจะมีการพิจารณาเฉพาะที่ทำให้หนึ่งในคอลเลกชันอื่น ๆ ที่ดีกว่า - ตัวอย่างเช่นคุณอาจเลือกStreamถ้าคุณต้องการการประเมินผลขี้เกียจและแคช ( Iteratorเป็นเร็วขึ้น แต่ไม่ได้แคช) หรือListถ้า อัลกอริทึมถูกนำมาใช้ตามธรรมชาติกับการดำเนินงานที่ฉันกล่าวถึง

อย่างไรก็ตามคุณควรใช้SeqหรือIndexedSeqยกเว้นว่าคุณต้องการชิ้นส่วนเฉพาะของ API (เช่นList's ::) หรือแม้กระทั่งGenSeqหรือGenIndexedSeqหากอัลกอริทึมของคุณสามารถทำงานแบบขนาน


3
ขอบคุณสำหรับคำตอบ. คุณมีความหมายว่า "มีท้องที่ที่ดีมาก"
Ngoc Dao

10
@ngocdaothanh หมายความว่ามีการจัดกลุ่มข้อมูลไว้ในหน่วยความจำอย่างใกล้ชิดช่วยเพิ่มโอกาสที่ข้อมูลจะอยู่ในแคชเมื่อคุณต้องการ
Daniel C. Sobral

1
@ user247077 ใช่รายการต่างๆสามารถเอาชนะ Vectors ได้อย่างมีประสิทธิภาพตามที่ระบุไว้ในรายการ และไม่ได้ทุกการกระทำของเวกเตอร์ตัดจำหน่าย O (1) ในความเป็นจริงในโครงสร้างข้อมูลที่ไม่เปลี่ยนรูป (ซึ่งเป็นกรณี) การแทรก / ลบทางเลือกที่ปลายทั้งสองจะไม่ตัดจำหน่ายเลย ในกรณีดังกล่าวแคชไม่มีประโยชน์เพราะคุณมักจะคัดลอกเวกเตอร์
Daniel C. Sobral

1
@ user247077 บางทีคุณอาจไม่ทราบว่าVectorเป็นโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบใน Scala?
Daniel C. Sobral

1
@ user247077 เป็นวิธีที่ซับซ้อนกว่านั้นรวมถึงบางสิ่งที่ไม่แน่นอนภายในที่จะผนวกราคาถูก แต่เมื่อคุณใช้มันเป็นกองซึ่งเป็นรายการสถานการณ์ที่ดีที่สุดที่ไม่เปลี่ยนรูปคุณยังคงมีลักษณะหน่วยความจำเดียวกันของรายการที่เชื่อมโยง แต่ ด้วยโปรไฟล์การจัดสรรหน่วยความจำที่ใหญ่กว่ามาก
Daniel C. Sobral

29

ข้อความบางส่วนในที่นี้ทำให้เกิดความสับสนหรือผิดพลาดโดยเฉพาะอย่างยิ่งความคิดที่ว่าไม่เปลี่ยนรูปแบบผู้ตรวจการใน Scala เป็นอะไรที่เหมือนกับ ArrayList รายการและเวกเตอร์มีทั้งโครงสร้างข้อมูลที่เปลี่ยนแปลงไม่ได้ถาวร (เช่น "ถูกเพื่อรับสำเนาที่แก้ไข") ไม่มีตัวเลือกเริ่มต้นที่สมเหตุสมผลเนื่องจากอาจเป็นโครงสร้างข้อมูลที่ไม่แน่นอน แต่ขึ้นอยู่กับว่าอัลกอริทึมของคุณกำลังทำอะไร รายการเป็นรายการที่เชื่อมโยงเดี่ยว ๆ ในขณะที่ Vector เป็น base-32 integer trie นั่นคือโครงสร้างการค้นหาที่มีโหนดของระดับ 32 การใช้โครงสร้างนี้ Vector สามารถให้การดำเนินการทั่วไปได้อย่างรวดเร็วเช่นใน O (log_32 ( n)) ใช้งานได้กับการผนวกผนวกผนวกการเข้าถึงแบบสุ่มการแยกส่วนในหัว / หาง การวนซ้ำตามลำดับคือเส้นตรง รายการในอีกทางหนึ่งเพียงแค่ให้การทำซ้ำเชิงเส้นและเวลาคงที่ล่วงหน้าการสลายตัวในหัว / หาง

นี่อาจดูเหมือนกับว่า Vector เป็นตัวแทนที่ที่ดีสำหรับ List ในเกือบทุกกรณี แต่ prepend, การสลายตัวและการวนซ้ำมักจะเป็นสิ่งสำคัญในการดำเนินการลำดับในโปรแกรมการทำงานและค่าคงที่ของการดำเนินการเหล่านี้ โครงสร้างที่ซับซ้อนมากขึ้น ฉันทำการวัดสองสามครั้งดังนั้นการวนซ้ำเร็วขึ้นเป็นสองเท่าสำหรับรายการก่อนหน้านี้จะเร็วกว่ารายการประมาณ 100 เท่าการสลายตัวที่หัว / หางจะเร็วกว่าประมาณ 10 เท่าสำหรับรายการและการสร้างจากเวกเตอร์ที่เคลื่อนที่ได้เร็วขึ้นประมาณ 2 เท่า (นี่อาจเป็นเพราะ Vector สามารถจัดสรรอาร์เรย์ของ 32 องค์ประกอบพร้อมกันเมื่อคุณสร้างมันขึ้นมาโดยใช้ตัวสร้างแทนการเพิ่มหรือผนวกองค์ประกอบทีละตัว)

ดังนั้นโครงสร้างข้อมูลที่เราควรใช้? โดยทั่วไปมีสี่กรณีทั่วไป:

  • เราจำเป็นต้องแปลงลำดับโดยการดำเนินการเช่นแผนที่ตัวกรองการพับเป็นต้นโดยทั่วไปไม่สำคัญว่าเราควรเขียนโปรแกรมอัลกอริทึมของเราโดยทั่วไปและอาจได้รับประโยชน์จากการยอมรับลำดับคู่ขนาน สำหรับการดำเนินการตามลำดับรายการอาจจะเร็วกว่าเล็กน้อย แต่คุณควรเปรียบเทียบถ้าคุณต้องปรับให้เหมาะสม
  • เราต้องการการเข้าถึงแบบสุ่มและการอัปเดตที่แตกต่างกันมากมายดังนั้นเราควรใช้เวกเตอร์รายชื่อจะช้าอย่างเด็ดขาด
  • เราดำเนินการกับรายการในลักษณะการทำงานแบบคลาสสิกการสร้างรายการโดยการเติมและวนซ้ำโดยการย่อยสลายแบบเรียกซ้ำ: รายการการใช้งานเวกเตอร์จะช้าลงด้วยปัจจัย 10-100 หรือมากกว่า
  • เรามีอัลกอริทึมที่สำคัญเกี่ยวกับประสิทธิภาพซึ่งจำเป็นอย่างยิ่งและมีการเข้าถึงแบบสุ่มในรายการบางอย่างเช่นจัดเรียงอย่างรวดเร็ว: ใช้โครงสร้างข้อมูลที่จำเป็นเช่น ArrayBuffer ภายในเครื่องและคัดลอกข้อมูลของคุณจากและไปยัง

24

สำหรับคอลเลกชันที่ไม่เปลี่ยนรูปถ้าคุณต้องการลำดับการตัดสินใจหลักของคุณคือใช้IndexedSeqหรือ a LinearSeqซึ่งให้การรับประกันที่แตกต่างกันสำหรับประสิทธิภาพ IndexedSeq ให้การเข้าถึงองค์ประกอบแบบสุ่มอย่างรวดเร็วและการดำเนินการที่รวดเร็ว LinearSeq ให้การเข้าถึงที่รวดเร็วเฉพาะกับองค์ประกอบแรกผ่านทางheadแต่ยังมีการtailดำเนินการที่รวดเร็ว (นำมาจากเอกสาร Seq)

สำหรับคุณมักจะเลือกIndexedSeq s และs ยังเป็น IndexedSeq ด้วยVectorRangeWrappedString

สำหรับ a LinearSeqโดยปกติแล้วคุณจะเลือก a Listหรือเทียบเท่าStreamเสมอ ตัวอย่างอื่น ๆ คือQueues และStacks

ดังนั้นในแง่ชวาArrayListใช้ในทำนองเดียวกันกับสกาล่าของVectorและในทำนองเดียวกันกับสกาล่าของLinkedList Listแต่ใน Scala ฉันมักจะใช้ List บ่อยกว่า Vector เพราะ Scala ได้รับการสนับสนุนที่ดีกว่าสำหรับฟังก์ชั่นที่มีการข้ามผ่านของลำดับเช่นการแมปการพับการวนซ้ำเป็นต้นคุณจะมีแนวโน้มที่จะใช้ฟังก์ชันเหล่านี้เพื่อจัดการรายการเป็น ทั้งหมดแทนที่จะสุ่มเข้าถึงองค์ประกอบแต่ละรายการ


แต่ถ้าการวนซ้ำของ Vector เร็วกว่าลิสต์ของและฉันสามารถแมปโฟลด์ ฯลฯ ได้เช่นกันนอกเหนือจากกรณีพิเศษบางอย่าง
Duncan McGregor

@Duncan คุณเคยได้ยินที่ไหนว่าการวนซ้ำของ Vector เร็วกว่าใคร สำหรับการเริ่มต้นคุณต้องติดตามและอัปเดตดัชนีปัจจุบันซึ่งคุณไม่จำเป็นต้องมีรายการที่เชื่อมโยง ฉันจะไม่เรียกฟังก์ชั่นรายการ "กรณีพิเศษ" - พวกเขาเป็นขนมปังและเนยของการเขียนโปรแกรมการทำงาน การไม่ใช้มันจะเหมือนกับการพยายามเขียนโปรแกรม Java โดยไม่มี for- หรือ while-loops
Luigi Plinge

2
ผมค่อนข้างมั่นใจว่าVectorเป็นซ้ำเป็นเร็วขึ้น แต่คนต้องการที่จะมาตรฐานมันเพื่อให้แน่ใจว่า
Daniel Spiewak

ฉันคิดว่าองค์ประกอบ (?) ในVectorร่างกายมีอยู่ด้วยกันบน RAM ในกลุ่มที่ 32 ซึ่งพอดีกับแคชของ 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) ฉันได้รับ List [Int] ไม่ใช่ Vector [Int]
Duncan McGregor

2
IndexedSeqหากคุณมีการเข้าถึงแบบสุ่มใช้ ซึ่งก็เป็นเช่นVectorนั้น แต่นั่นเป็นอีกเรื่องหนึ่ง
Daniel C. Sobral

@DuncanMcGregor: เวกเตอร์เป็นค่าเริ่มต้นซึ่งการดำเนินการIndexedSeq เป็นที่จะดำเนินการใช้ SeqSeq(1, 2, 3)LinearSeqList
pathikrit

0

หากคุณกำลังเขียนโปรแกรมอย่างไม่รู้จักเหน็ดเหนื่อยและต้องการการเข้าถึงแบบสุ่ม Seq เป็นหนทางที่จะไป มิฉะนั้นรายการใช้งานได้ดียกเว้นการดำเนินการจะไม่สามารถทำการขนานกันได้

หากคุณไม่ต้องการโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบให้ติดกับ ArrayBuffer เนื่องจากเป็น Scala ที่เทียบเท่ากับ ArrayList


ฉันกำลังยึดติดกับอาณาจักรของคอลเล็คชั่นที่ไม่เปลี่ยนแปลงและต่อเนื่อง ประเด็นของฉันคือแม้ว่าฉันไม่ต้องการเข้าถึงแบบสุ่ม แต่ Vector ได้แทนที่รายการอย่างมีประสิทธิภาพหรือไม่
Duncan McGregor

2
ขึ้นอยู่กับกรณีการใช้งาน เวกเตอร์มีความสมดุลมากขึ้น การวนซ้ำเร็วกว่ารายการและการเข้าถึงแบบสุ่มนั้นเร็วกว่ามาก การอัปเดตช้าลงเนื่องจากไม่ใช่เพียงรายการเสริม แต่จะเป็นการอัปเดตจำนวนมากจากการพับซึ่งสามารถทำได้ด้วยตัวสร้าง ที่กล่าวว่าฉันคิดว่า Vector เป็นตัวเลือกเริ่มต้นที่ดีที่สุดเนื่องจากมีประโยชน์หลากหลาย
Joshua Hartman

ที่ฉันคิดว่าจะเป็นหัวใจของคำถามของฉัน - เวกเตอร์เป็นสิ่งที่ดีที่เราอาจใช้พวกเขาที่ตัวอย่างมักจะแสดงรายการ
Duncan McGregor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.