เมื่อใดที่ฉันควรใช้รายการเทียบกับรายการที่เชื่อมโยง


392

เมื่อมันจะดีกว่าที่จะใช้รายการเอสLinkedList ?


3
Java q , ไม่ควรแตกต่างกันมาก
nawfal

1
@ jonathan-allen โปรดลองเปลี่ยนคำตอบที่ยอมรับ ปัจจุบันไม่ถูกต้องและทำให้เข้าใจผิดอย่างมาก
Xpleria

คำตอบ:


107

แก้ไข

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

คำตอบเดิม ...

ฉันพบผลลัพธ์ที่น่าสนใจ:

// Temporary class to show the example
class Temp
{
    public decimal A, B, C, D;

    public Temp(decimal a, decimal b, decimal c, decimal d)
    {
        A = a;            B = b;            C = c;            D = d;
    }
}

รายการที่เชื่อมโยง (3.9 วินาที)

        LinkedList<Temp> list = new LinkedList<Temp>();

        for (var i = 0; i < 12345678; i++)
        {
            var a = new Temp(i, i, i, i);
            list.AddLast(a);
        }

        decimal sum = 0;
        foreach (var item in list)
            sum += item.A;

รายการ (2.4 วินาที)

        List<Temp> list = new List<Temp>(); // 2.4 seconds

        for (var i = 0; i < 12345678; i++)
        {
            var a = new Temp(i, i, i, i);
            list.Add(a);
        }

        decimal sum = 0;
        foreach (var item in list)
            sum += item.A;

แม้ว่าคุณจะเข้าถึงข้อมูลเป็นหลักมันช้ากว่ามาก !! ฉันบอกว่าไม่เคยใช้รายการที่ลิงก์




ต่อไปนี้เป็นการเปรียบเทียบการทำงานของเม็ดมีดจำนวนมาก (เราวางแผนที่จะแทรกรายการที่ตรงกลางของรายการ)

รายการที่เชื่อมโยง (51 วินาที)

        LinkedList<Temp> list = new LinkedList<Temp>();

        for (var i = 0; i < 123456; i++)
        {
            var a = new Temp(i, i, i, i);

            list.AddLast(a);
            var curNode = list.First;

            for (var k = 0; k < i/2; k++) // In order to insert a node at the middle of the list we need to find it
                curNode = curNode.Next;

            list.AddAfter(curNode, a); // Insert it after
        }

        decimal sum = 0;
        foreach (var item in list)
            sum += item.A;

รายการ (7.26 วินาที)

        List<Temp> list = new List<Temp>();

        for (var i = 0; i < 123456; i++)
        {
            var a = new Temp(i, i, i, i);

            list.Insert(i / 2, a);
        }

        decimal sum = 0;
        foreach (var item in list)
            sum += item.A;

รายการที่เชื่อมโยงมีการอ้างอิงตำแหน่งที่จะแทรก (.04 วินาที)

        list.AddLast(new Temp(1,1,1,1));
        var referenceNode = list.First;

        for (var i = 0; i < 123456; i++)
        {
            var a = new Temp(i, i, i, i);

            list.AddLast(a);
            list.AddBefore(referenceNode, a);
        }

        decimal sum = 0;
        foreach (var item in list)
            sum += item.A;

ดังนั้นหากคุณวางแผนที่จะแทรกหลายรายการและคุณยังมีที่อ้างอิงที่คุณวางแผนแทรกรายการจากนั้นใช้รายการที่เชื่อมโยง เพียงเพราะคุณต้องแทรกรายการจำนวนมากมันไม่ได้ทำให้เร็วขึ้นเพราะการค้นหาตำแหน่งที่คุณต้องการแทรกต้องใช้เวลา


99
มีประโยชน์อย่างหนึ่งต่อ LinkedList ผ่านรายการ (นี่คือ. net เฉพาะ): เนื่องจาก List ได้รับการสนับสนุนโดยอาร์เรย์ภายในจึงถูกจัดสรรในบล็อกที่ต่อเนื่องกันหนึ่งบล็อก หากบล็อกที่จัดสรรนั้นมีขนาดเกิน 85,000 ไบต์บล็อกนั้นจะถูกจัดสรรใน Large Object Heap ซึ่งเป็นรุ่นที่ไม่สามารถบีบอัดได้ ขึ้นอยู่กับขนาดสิ่งนี้สามารถนำไปสู่การแตกแฟรกเมนต์ของฮีปซึ่งเป็นหน่วยความจำรั่วเล็กน้อย
JerKimball

35
โปรดทราบว่าหากคุณกำลังเตรียมการล่วงหน้าจำนวนมาก (ในขณะที่คุณกำลังทำในตัวอย่างล่าสุด) หรือลบรายการแรกรายการที่เชื่อมโยงจะเกือบจะเร็วขึ้นอย่างมีนัยสำคัญเนื่องจากไม่มีการค้นหาหรือย้าย / คัดลอกสิ่งที่ต้องทำ รายการจะต้องย้ายทุกอย่างขึ้นจุดเพื่อรองรับรายการใหม่ทำให้การดำเนินการเตรียมการ (N)
cHao

6
ทำไม in-loop list.AddLast(a);ในสองตัวอย่าง LinkedList ล่าสุด ฉันจะทำมันก่อนที่จะวนรอบเช่นเดียวกับlist.AddLast(new Temp(1,1,1,1));ในถัดจาก LinkedList ที่ผ่านมา แต่ดูเหมือน (กับฉัน) เช่นคุณกำลังเพิ่มสองวัตถุ Temp มากในลูปตัวเอง (และเมื่อฉันตรวจสอบตัวเองอีกครั้งด้วยแอปทดสอบแน่นอนว่ามีมากเป็นสองเท่าในรายการที่
เชื่อมโยง

7
ฉันลงคะแนนคำตอบนี้ 1) คำแนะนำทั่วไปของคุณI say never use a linkedList.มีข้อบกพร่องตามที่คุณโพสต์ในภายหลังเผยให้เห็น คุณอาจต้องการแก้ไข 2) คุณจับเวลาอะไร การเริ่มต้นการเพิ่มและการนับรวมเข้าด้วยกันในขั้นตอนเดียว ส่วนใหญ่การสร้างอินสแตนซ์และการแจงนับไม่ใช่สิ่งที่ ppl กังวลเกี่ยวกับสิ่งเหล่านี้เป็นขั้นตอนเดียว กำหนดเวลาการแทรกและส่วนเพิ่มเติมโดยเฉพาะจะให้แนวคิดที่ดีกว่า 3) ที่สำคัญที่สุดคือคุณกำลังเพิ่มมากกว่าที่จำเป็นในรายการที่เชื่อมโยง นี่เป็นการเปรียบเทียบที่ผิด เผยแพร่แนวคิดที่ผิดเกี่ยวกับลิสต์ที่ลิงก์
nawfal

47
ขออภัยคำตอบนี้ไม่ดีจริงๆ โปรดอย่าฟังคำตอบนี้ เหตุผลสั้น: มันมีข้อบกพร่องอย่างสมบูรณ์ที่จะคิดว่าการใช้งานรายการที่ได้รับการสนับสนุนอาร์เรย์นั้นโง่พอที่จะปรับขนาดอาร์เรย์ในแต่ละการแทรก รายการที่เชื่อมโยงนั้นช้ากว่ารายการที่ได้รับการสำรองข้อมูลเมื่อทำการสำรวจเช่นเดียวกับเมื่อแทรกที่ปลายทั้งสองเพราะพวกเขาเพียงต้องการสร้างวัตถุใหม่ในขณะที่รายการที่ได้รับการสำรองข้อมูลจะใช้บัฟเฟอร์ (ทั้งสองทิศทาง มาตรฐาน (ทำไม่ดี) บ่งชี้อย่างชัดเจนว่า คำตอบทั้งหมดล้มเหลวในการตรวจสอบกรณีที่รายการเชื่อมโยงจะดีกว่า!
mafu

277

ในกรณีส่วนใหญ่List<T>มีประโยชน์มากกว่า LinkedList<T>จะมีค่าใช้จ่ายน้อยลงเมื่อเพิ่ม / ลบรายการที่อยู่ตรงกลางของรายการในขณะที่List<T>สามารถเพิ่ม / ลบรายการที่ถูกที่สุดในตอนท้ายของรายการเท่านั้น

LinkedList<T>จะมีประสิทธิภาพที่สุดก็ต่อเมื่อคุณกำลังเข้าถึงข้อมูลตามลำดับ (ไปข้างหน้าหรือข้างหลัง) - การเข้าถึงแบบสุ่มค่อนข้างแพงเนื่องจากจะต้องเดินผ่านสายโซ่ในแต่ละครั้ง (เหตุใดจึงไม่มีดัชนี) อย่างไรก็ตามเนื่องจาก a List<T>เป็นหลักเพียงแค่การเข้าถึงแบบสุ่ม (ด้วย wrapper) เป็นเรื่องปกติ

List<T>นอกจากนี้ยังมีจำนวนมากวิธีการสนับสนุน - Find, ToArrayฯลฯ ; อย่างไรก็ตามสิ่งเหล่านี้สามารถใช้ได้LinkedList<T>กับ. NET 3.5 / C # 3.0 ผ่านวิธีการขยาย - ดังนั้นจึงมีปัจจัยน้อยกว่า


4
ข้อดีอย่างหนึ่งของรายการ <> กับ LinkedList <> ที่ฉันไม่เคยนึกถึงมาก่อนเกี่ยวกับวิธีที่ไมโครโปรเซสเซอร์ใช้การแคชหน่วยความจำ ถึงแม้ว่าผมจะไม่เข้าใจมันอย่างสมบูรณ์นักเขียนของบทความบล็อกนี้พูดมากเกี่ยวกับ "บ้านใกล้เรือนเคียงของการอ้างอิง" ซึ่งจะทำให้ภายในอาร์เรย์มากเร็วกว่า traversing รายการที่เชื่อมโยงอย่างน้อยถ้ารายการที่เชื่อมโยงได้มีการแยกส่วนบ้างในหน่วยความจำ . kjellkod.wordpress.com/2012/02/25/…
RenniePet

@RenniePet List ใช้งานกับอาเรย์แบบไดนามิกและอาร์เรย์เป็นบล็อกหน่วยความจำต่อเนื่อง
Casey

2
เนื่องจาก List เป็นอาร์เรย์แบบไดนามิกนั่นเป็นเหตุผลว่าทำไมบางครั้งมันก็ดีที่จะระบุความสามารถของ List ใน Constructor หากคุณรู้ก่อน
Cardin Lee JH

เป็นไปได้หรือไม่ที่การใช้ C # ของทั้งหมด, array, List <T> และ LinkedList <T> นั้นค่อนข้างไม่ดีสำหรับกรณีที่สำคัญมากหนึ่งกรณี: คุณต้องการรายการที่มีขนาดใหญ่มากผนวก (AddLast) และ Traversal ตามลำดับ (ในทิศทางเดียว) ดีโดยสิ้นเชิง: ฉันไม่ต้องการการปรับขนาดอาร์เรย์เพื่อรับบล็อกต่อเนื่อง (มันรับประกันสำหรับแต่ละแถวแม้กระทั่งอาร์เรย์ 20 GB หรือไม่) และฉันไม่รู้ขนาดล่วงหน้า แต่ฉันสามารถเดาขนาดบล็อกล่วงหน้าได้เช่น 100 MB เพื่อจองล่วงหน้าทุกครั้ง นี่จะเป็นการดำเนินการที่ดี หรือเป็นอาเรย์ / รายการคล้ายกับที่และฉันพลาดจุดหรือไม่
Philm

1
@Philm เป็นสถานการณ์จำลองที่คุณเขียน shim ของคุณเองเหนือกลยุทธ์บล็อกที่คุณเลือก List<T>และT[]จะล้มเหลวในการเป็นก้อน (แผ่นเดียวทั้งหมด) LinkedList<T>จะครวญครางเพราะละเอียดเกินไป (แผ่นต่อองค์ประกอบ)
Marc Gravell

212

การคิดถึงรายการที่เชื่อมโยงเป็นรายการอาจทำให้เข้าใจผิดเล็กน้อย มันเหมือนโซ่มากกว่า ในความเป็นจริงใน. NET LinkedList<T>ไม่ได้ใช้IList<T>ไม่ได้ดำเนินการไม่มีแนวคิดที่แท้จริงของดัชนีในรายการที่เชื่อมโยงแม้ว่ามันอาจดูเหมือนว่ามี แน่นอนว่าไม่มีวิธีการใด ๆ ในชั้นเรียนที่ยอมรับดัชนี

รายการที่เชื่อมโยงอาจถูกลิงค์โดยลำพังหรือเชื่อมโยงซ้ำ สิ่งนี้หมายถึงว่าแต่ละองค์ประกอบในกลุ่มนี้มีลิงก์ไปยังองค์ประกอบถัดไปเท่านั้น (เชื่อมโยงโดยลำพัง) หรือทั้งสององค์ประกอบก่อนหน้า / ถัดไป (ลิงก์สองเท่า) LinkedList<T>มีการเชื่อมโยงทวีคูณ

ภายในList<T>ได้รับการสนับสนุนโดยอาเรย์ นี่เป็นการนำเสนอที่มีขนาดกะทัดรัดมากในหน่วยความจำ ตรงกันข้ามLinkedList<T>เกี่ยวข้องกับหน่วยความจำเพิ่มเติมเพื่อจัดเก็บลิงค์แบบสองทิศทางระหว่างองค์ประกอบต่อเนื่อง ดังนั้นLinkedList<T>โดยทั่วไปรอยความทรงจำของ a จะใหญ่กว่าList<T>(ด้วยข้อแม้ที่List<T>สามารถมีองค์ประกอบอาร์เรย์ภายในที่ไม่ได้ใช้เพื่อปรับปรุงประสิทธิภาพในระหว่างการดำเนินการต่อท้าย)

พวกมันมีคุณสมบัติด้านประสิทธิภาพที่แตกต่างเช่นกัน:

ผนวก

  • LinkedList<T>.AddLast(item) เวลาคงที่
  • List<T>.Add(item) เวลาคงที่ตัดจำหน่ายกรณีเชิงเส้นที่เลวร้ายที่สุด

ย่อหน้า

  • LinkedList<T>.AddFirst(item) เวลาคงที่
  • List<T>.Insert(0, item) เวลาเชิงเส้น

การแทรก

  • LinkedList<T>.AddBefore(node, item) เวลาคงที่
  • LinkedList<T>.AddAfter(node, item) เวลาคงที่
  • List<T>.Insert(index, item) เวลาเชิงเส้น

การถอด

  • LinkedList<T>.Remove(item) เวลาเชิงเส้น
  • LinkedList<T>.Remove(node) เวลาคงที่
  • List<T>.Remove(item) เวลาเชิงเส้น
  • List<T>.RemoveAt(index) เวลาเชิงเส้น

นับ

  • LinkedList<T>.Count เวลาคงที่
  • List<T>.Count เวลาคงที่

มี

  • LinkedList<T>.Contains(item) เวลาเชิงเส้น
  • List<T>.Contains(item) เวลาเชิงเส้น

ชัดเจน

  • LinkedList<T>.Clear() เวลาเชิงเส้น
  • List<T>.Clear() เวลาเชิงเส้น

อย่างที่คุณเห็นพวกมันส่วนใหญ่จะเทียบเท่ากัน ในทางปฏิบัติ API ของLinkedList<T>นั้นยุ่งยากกว่าการใช้งานและรายละเอียดของความต้องการภายในรั่วไหลออกมาเป็นรหัสของคุณ

อย่างไรก็ตามหากคุณต้องการแทรก / ลบหลายรายการจากภายในรายการจะมีเวลาคงที่ List<T>เสนอเวลาเชิงเส้นเนื่องจากรายการพิเศษในรายการจะต้องถูกสับหลังจากการแทรก / กำจัด


2
การนับลิสต์รายการคงที่หรือไม่ ฉันคิดว่าจะเป็นเส้นตรง?
เลน Ballard

10
@Iain นับถูกแคชในคลาสรายการทั้งสอง
Drew Noakes

3
คุณเขียนว่า "List <T> .Add (รายการ) เวลาลอการิทึม" แต่ในความเป็นจริง "คงที่" ถ้าความสามารถของรายการสามารถเก็บรายการใหม่และ "Linear" หากรายการมีพื้นที่ไม่เพียงพอและใหม่ ที่จะจัดสรรใหม่
aStranger

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

1
@Philm คุณควรเริ่มต้นคำถามใหม่และคุณไม่ได้บอกว่าคุณจะใช้โครงสร้างข้อมูลนี้อย่างไรเมื่อสร้างขึ้นมา แต่ถ้าคุณพูดถึงล้านแถวคุณอาจชอบไฮบริดบางส่วน (รายการที่เชื่อมโยงของ อาเรย์ chunks หรือคล้ายกัน) เพื่อลดการกระจายตัวของฮีปลดค่าใช้จ่ายหน่วยความจำและหลีกเลี่ยงวัตถุขนาดใหญ่บน LOH
Drew Noakes

118

รายการที่เชื่อมโยงให้การแทรกหรือลบรายการสมาชิกอย่างรวดเร็ว สมาชิกแต่ละคนในรายการที่เชื่อมโยงประกอบด้วยตัวชี้ไปยังสมาชิกถัดไปในรายการเพื่อแทรกสมาชิกที่ตำแหน่ง i:

  • อัปเดตตัวชี้ในสมาชิก i-1 เพื่อชี้ไปที่สมาชิกใหม่
  • ตั้งค่าตัวชี้ในสมาชิกใหม่ให้ชี้ไปที่สมาชิก i

ข้อเสียของรายการที่เชื่อมโยงคือการเข้าถึงแบบสุ่มไม่สามารถทำได้ การเข้าถึงสมาชิกต้องผ่านรายการจนกว่าจะพบสมาชิกที่ต้องการ


6
ฉันจะเพิ่มว่ารายการที่เชื่อมโยงมีค่าใช้จ่ายต่อรายการที่เก็บไว้โดยนัยข้างต้นผ่าน LinkedListNode ซึ่งอ้างอิงโหนดก่อนหน้าและถัดไป ผลตอบแทนจากการที่เป็นบล็อกของหน่วยความจำที่ต่อเนื่องกันไม่จำเป็นต้องใช้ในการจัดเก็บรายการซึ่งแตกต่างจากรายการตามอาร์เรย์
paulecoyote

3
การบล็อกหน่วยความจำที่ต่อเนื่องกันมักจะไม่ได้รับการอ้างอิงหรือไม่
Jonathan Allen

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

6
หากคุณเคยทำงานกับอาร์เรย์หรือรายการที่มีขนาดใหญ่มาก (รายการเพียงแค่ล้อมรอบอาร์เรย์) คุณจะเริ่มพบปัญหาหน่วยความจำแม้ว่าจะมีหน่วยความจำเหลืออยู่บนเครื่องของคุณ รายการใช้กลยุทธ์การเพิ่มเป็นสองเท่าเมื่อจัดสรรพื้นที่ใหม่ในอาเรย์ต้นแบบ ดังนั้นอาร์เรย์ 1000000 elemnt ที่เต็มจะถูกคัดลอกไปยังอาร์เรย์ใหม่ที่มีองค์ประกอบ 2000000 อาร์เรย์ใหม่นี้จำเป็นต้องสร้างขึ้นในพื้นที่หน่วยความจำต่อเนื่องที่มีขนาดใหญ่พอที่จะรองรับมันได้
Andrew

1
ผมมีกรณีเฉพาะที่ฉันไม่ได้เพิ่มและลบและวนรอบหนึ่งโดยหนึ่ง ... ที่นี่เป็นรายการที่เชื่อมโยงไกลกว่ารายการปกติ ..
ปีเตอร์

26

คำตอบก่อนหน้าของฉันไม่ถูกต้องเพียงพอ มันน่ากลัวจริงๆ: D แต่ตอนนี้ฉันสามารถโพสต์คำตอบที่มีประโยชน์และถูกต้องได้มากขึ้น


ฉันทำการทดสอบเพิ่มเติม คุณสามารถค้นหาแหล่งที่มาได้จากลิงค์ต่อไปนี้และตรวจสอบอีกครั้งในสภาพแวดล้อมของคุณด้วยตัวคุณเอง: https://github.com/ukushu/DataStructuresTestsAndOther.git

ผลสรุป:

  • อาร์เรย์ต้องใช้:

    • บ่อยครั้งที่สุด มันเร็วและใช้ช่วง RAM ที่เล็กที่สุดสำหรับข้อมูลจำนวนเดียวกัน
    • หากคุณรู้จำนวนเซลล์ที่แน่นอนที่ต้องการ
    • ถ้าข้อมูลถูกบันทึกในอาร์เรย์ <85000 b (85000/32 = 2656 องค์ประกอบสำหรับข้อมูลจำนวนเต็ม)
    • หากต้องการความเร็วในการเข้าถึงสูงแบบสุ่ม
  • รายการจำเป็นต้องใช้:

    • หากจำเป็นต้องเพิ่มเซลล์ในตอนท้ายของรายการ (บ่อยครั้ง)
    • หากจำเป็นต้องเพิ่มเซลล์ในต้น / กลางของรายการ (ไม่ได้ออฟ)
    • ถ้าข้อมูลถูกบันทึกในอาร์เรย์ <85000 b (85000/32 = 2656 องค์ประกอบสำหรับข้อมูลจำนวนเต็ม)
    • หากต้องการความเร็วในการเข้าถึงสูงแบบสุ่ม
  • LinkedList ต้องใช้:

    • หากจำเป็นต้องเพิ่มเซลล์ในต้น / กลาง / ปลายของรายการ (มัก)
    • หากจำเป็นต้องเข้าถึงแบบต่อเนื่องเท่านั้น (ไปข้างหน้า / ข้างหลัง)
    • หากคุณต้องการบันทึกรายการขนาดใหญ่ แต่จำนวนรายการต่ำ
    • อย่าใช้ดีกว่าสำหรับรายการจำนวนมากเพราะใช้หน่วยความจำเพิ่มเติมสำหรับลิงค์

รายละเอียดเพิ่มเติม:

введитесюдаописаниеизображения เรื่องน่ารู้:

  1. LinkedList<T>ภายในไม่ใช่รายการใน. NET IList<T>ก็จะยิ่งไม่ใช้ และนั่นเป็นสาเหตุที่มีดัชนีขาดและวิธีการที่เกี่ยวข้องกับดัชนี

  2. LinkedList<T>คือการรวบรวมตามโหนด ใน. NET มันมีการเชื่อมโยงการใช้งานทวีคูณ ซึ่งหมายความว่าองค์ประกอบก่อนหน้า / ถัดไปมีลิงก์ไปยังองค์ประกอบปัจจุบัน และข้อมูลมีการแยกส่วน - วัตถุรายการที่แตกต่างสามารถอยู่ในตำแหน่งที่แตกต่างกันของ RAM นอกจากนี้จะมีหน่วยความจำมากกว่าที่ใช้LinkedList<T>สำหรับList<T>หรืออาร์เรย์

  3. List<T>ในสุทธิเป็นทางเลือกของ Java ArrayList<T>ของ นี่หมายความว่านี่คืออาร์คเกอร์แรปเปอร์ ดังนั้นมันจึงถูกจัดสรรในหน่วยความจำให้เป็นหนึ่งบล็อกข้อมูลที่ต่อเนื่องกัน หากขนาดข้อมูลที่จัดสรรเกิน 85,000 ไบต์จะถูกย้ายไปที่ Large Object Heap ขึ้นอยู่กับขนาดสิ่งนี้สามารถนำไปสู่การแตกแฟรกเมนต์ของฮีป (แบบหน่วยความจำรั่วเล็กน้อย) แต่ในเวลาเดียวกันถ้าขนาด <85000 ไบต์ - นี่เป็นการนำเสนอขนาดกะทัดรัดและการเข้าถึงที่รวดเร็วในหน่วยความจำ

  4. Single block ที่ต่อเนื่องกันเป็นที่ต้องการสำหรับการเข้าถึงแบบสุ่มและการใช้หน่วยความจำ แต่สำหรับคอลเลกชันที่ต้องเปลี่ยนขนาดเป็นประจำโครงสร้างเช่น Array โดยทั่วไปจะต้องคัดลอกไปยังตำแหน่งใหม่ในขณะที่รายการที่เชื่อมโยงจะต้องจัดการหน่วยความจำ / ลบโหนด


1
คำถาม: ด้วย "ข้อมูลที่บันทึกในอาเรย์ <หรือ> 85.000 ไบต์" คุณหมายถึงข้อมูลต่ออาเรย์ / ลิสต์รายการคุณทำอะไร เป็นที่เข้าใจได้ว่าคุณหมายถึงชุดข้อมูลของอาเรย์ทั้งหมด ..
Philm

องค์ประกอบอาร์เรย์ตั้งอยู่ในหน่วยความจำตามลำดับ ดังนั้นต่ออาเรย์ ฉันรู้เกี่ยวกับความผิดพลาดในตารางต่อมาฉันจะแก้ไขได้ :) (ฉันหวังว่า .... )
แอนดรู

เมื่อรายการมีการแทรกที่ช้าถ้ารายการมีการเปลี่ยนแปลงจำนวนมาก (แทรก / ลบจำนวนมาก) หน่วยความจำที่ถูกครอบครองโดยพื้นที่ที่ถูกลบถูกเก็บไว้และถ้าเป็นเช่นนั้น
Rob

18

ความแตกต่างระหว่างรายการและรายการที่เชื่อมโยงอยู่ในการนำไปปฏิบัติ List คือการรวบรวมตามอาเรย์ (ArrayList) LinkedList เป็นคอลเล็กชันที่ใช้ตัวชี้โหนด (LinkedListNode) ในการใช้ระดับ API ทั้งคู่มีความเหมือนกันมากเนื่องจากทั้งคู่ใช้อินเทอร์เฟซชุดเดียวกันเช่น ICollection, IEnumerable เป็นต้น

ความแตกต่างที่สำคัญเกิดขึ้นเมื่อประสิทธิภาพมีความสำคัญ ตัวอย่างเช่นหากคุณกำลังใช้งานรายการที่มีการดำเนินการ "INSERT" จำนวนมาก LinkedList จะมีประสิทธิภาพสูงกว่ารายการ เนื่องจาก LinkedList สามารถทำได้ในเวลา O (1) แต่ List อาจต้องขยายขนาดของอาเรย์พื้นฐาน สำหรับข้อมูลเพิ่มเติม / รายละเอียดคุณอาจต้องการอ่านความแตกต่างของอัลกอริทึมระหว่าง LinkedList และโครงสร้างข้อมูลอาร์เรย์ http://en.wikipedia.org/wiki/Linked_list and Array

หวังว่าจะช่วยได้


4
List <T> ใช้อาร์เรย์ (T []) ไม่ใช่ ArrayList การแทรกซ้ำ: การปรับขนาดอาร์เรย์ไม่ใช่ปัญหา (อัลกอริธึมการเพิ่มเป็นสองเท่าหมายความว่าส่วนใหญ่เวลาที่ไม่ต้องทำ): ปัญหาคือว่ามันจะต้องป้องกันการคัดลอกข้อมูลที่มีอยู่ทั้งหมดก่อนซึ่งใช้เวลาเพียงเล็กน้อย เวลา.
Marc Gravell

2
@Marc 'ที่ขั้นตอนวิธีการเสแสร้ง" เพียง แต่ทำให้ O (logn) แต่มันก็ยังคงเลวร้ายยิ่งกว่า O (1)
Ilya Ryzhenkov

2
ประเด็นของฉันคือว่าไม่ใช่การปรับขนาดที่ทำให้เกิดความเจ็บปวด - มันเป็นความผิดพลาด ดังนั้นกรณีที่เลวร้ายที่สุดถ้าเราเพิ่มองค์ประกอบแรก (zeroth) ในแต่ละครั้ง blit ต้องย้ายทุกอย่างทุกครั้ง
Marc Gravell

@IlyaRyzhenkov - คุณกำลังคิดเกี่ยวกับกรณีที่Addอยู่ในตอนท้ายของอาร์เรย์ที่มีอยู่เสมอ Listเป็น "ดีพอ" ที่ถึงแม้ว่าจะไม่ใช่ O (1) ปัญหาร้ายแรงเกิดขึ้นถ้าคุณต้องการหลายคนAddที่ยังไม่จบ Marc ชี้ให้เห็นว่าความต้องการย้ายข้อมูลที่มีอยู่ทุกครั้งที่คุณแทรก (ไม่ใช่เพียงเมื่อจำเป็นต้องปรับขนาด) เป็นต้นทุนประสิทธิภาพที่สำคัญListกว่า
ToolmakerSteve

ปัญหาคือสัญกรณ์ทฤษฎีบิ๊กโอไม่ได้บอกเรื่องราวทั้งหมด ในวิทยาการคอมพิวเตอร์ที่ทุกคนเคยใส่ใจ แต่มีอะไรมากกว่าที่จะเกี่ยวข้องกับเรื่องนี้ในโลกแห่งความเป็นจริง
MattE

11

ข้อได้เปรียบหลักของรายการที่ถูกเชื่อมโยงผ่านอาร์เรย์คือการเชื่อมโยงช่วยให้เราสามารถจัดเรียงรายการได้อย่างมีประสิทธิภาพ เซดจ์วิคพี 91


1
IMO นี่ควรเป็นคำตอบ LinkedList จะใช้เมื่อคำสั่งที่รับประกันนั้นสำคัญ
RBaarda

1
@RBaarda: ฉันไม่เห็นด้วย ขึ้นอยู่กับระดับที่เรากำลังพูดถึง ระดับอัลกอริทึมนั้นแตกต่างจากระดับการใช้งานเครื่อง สำหรับการพิจารณาความเร็วคุณต้องการหลังด้วย ตามที่ได้กล่าวไว้อาร์เรย์ได้ถูกนำมาใช้ในการเป็น "หนึ่งชิ้น" ของหน่วยความจำสิ่งที่เป็นข้อ จำกัด เพราะสิ่งนี้สามารถนำไปสู่การปรับขนาดและการปรับโครงสร้างหน่วยความจำใหม่โดยเฉพาะกับอาร์เรย์ที่มีขนาดใหญ่มาก หลังจากคิดไปสักพักโครงสร้างข้อมูลของตัวเองพิเศษรายการที่เชื่อมโยงของอาร์เรย์จะเป็นแนวคิดหนึ่งที่ให้การควบคุมความเร็วของการบรรจุเชิงเส้นและการเข้าถึงโครงสร้างข้อมูลที่มีขนาดใหญ่มากขึ้น
Philm

1
@Philm - ฉันอัปเดตความคิดเห็นของคุณแล้ว แต่ฉันต้องการชี้ให้เห็นว่าคุณกำลังอธิบายข้อกำหนดที่แตกต่าง สิ่งที่คำตอบคือรายการที่เชื่อมโยงนั้นมีข้อดีด้านประสิทธิภาพสำหรับอัลกอริทึมที่เกี่ยวข้องกับการจัดเรียงรายการจำนวนมาก ระบุว่าฉันตีความความคิดเห็นของ RBaarda เป็นการอ้างถึงความจำเป็นในการเพิ่ม / ลบรายการในขณะที่ยังคงสั่งซื้ออย่างต่อเนื่อง (เกณฑ์การเรียงลำดับ) ดังนั้นไม่ใช่แค่ "การเติมเชิงเส้น" รับสิ่งนี้รายการหมดเนื่องจากดัชนีไม่มีประโยชน์ (เปลี่ยนทุกครั้งที่คุณเพิ่มองค์ประกอบที่ใดก็ได้ แต่ที่ส่วนท้าย)
ToolmakerSteve

4

สถานการณ์ทั่วไปในการใช้ LinkedList เป็นดังนี้:

สมมติว่าคุณต้องการลบสตริงจำนวนมากออกจากรายการสตริงที่มีขนาดใหญ่ให้พูด 100,000 สตริงที่จะลบสามารถค้นหาได้ใน HashSet dic และเชื่อว่ารายการของสตริงนั้นมีอยู่ระหว่าง 30,000 ถึง 60,000 สตริงที่จะลบ

รายการประเภทที่ดีที่สุดสำหรับการจัดเก็บ 100,000 สายคืออะไร คำตอบคือ LinkedList หากพวกมันถูกเก็บไว้ใน ArrayList ให้ทำการวนซ้ำและกำจัด Strings ที่จับคู่กันซึ่งต้องใช้เวลาถึงหลายพันล้านการดำเนินการในขณะที่ใช้เพียง 100,000 การดำเนินการโดยใช้ iterator และ remove ()

LinkedList<String> strings = readStrings();
HashSet<String> dic = readDic();
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()){
    String string = iterator.next();
    if (dic.contains(string))
    iterator.remove();
}

6
คุณสามารถใช้RemoveAllเพื่อลบรายการออกจาก a Listโดยไม่ต้องย้ายรายการจำนวนมากหรือใช้Whereจาก LINQ เพื่อสร้างรายการที่สอง การใช้LinkedListที่นี่ แต่จบลงด้วยการบริโภคอย่างมากหน่วยความจำมากกว่าชนิดอื่น ๆ Listของคอลเลกชันและการสูญเสียของหน่วยความจำท้องถิ่นหมายถึงว่ามันจะเห็นได้ชัดช้าย้ำทำให้มันค่อนข้างเลวร้ายยิ่งกว่า
Servy

@Servy โปรดทราบว่าคำตอบของ @ Tom ใช้ Java ฉันไม่แน่ใจว่ามีRemoveAllJava ที่เทียบเท่าหรือไม่
Arturo Torres Sánchez

3
@ ArturoTorresSánchezคำถามที่เจาะจงนั้นระบุไว้ว่ามันเป็นเรื่องของ. NET ดังนั้นมันจึงทำให้คำตอบนั้นเหมาะสมน้อยกว่ากันมาก
บริการ

@Servy แล้วคุณควรจะพูดถึงมันตั้งแต่ต้น
Arturo Torres Sánchez

หากRemoveAllไม่มีให้ใช้Listคุณสามารถใช้อัลกอริทึม "การบดอัด" ซึ่งมีลักษณะคล้ายกับลูปของทอม แต่มีดัชนีสองตัวและต้องการย้ายไอเท็มเพื่อที่จะเก็บทีละรายการในอาร์เรย์ภายในของรายการ ประสิทธิภาพเป็น O (n) LinkedListเช่นเดียวกับอัลกอริทึมสำหรับทอม ในทั้งสองเวอร์ชันเวลาในการคำนวณคีย์ HashSet สำหรับสตริงจะมีผล นี่ไม่ใช่ตัวอย่างที่ดีว่าควรใช้เมื่อLinkedListใด
ToolmakerSteve

2

เมื่อคุณต้องการการเข้าถึงการจัดทำดัชนีในตัวการเรียงลำดับ (และหลังจากการค้นหาแบบไบนารีนี้) และ "ToArray ()" วิธีการคุณควรใช้รายการ


2

โดยพื้นฐานแล้ว a List<>ใน. NET คือ wrapper ทับอาเรย์ เป็นรายการที่เชื่อมโยงLinkedList<> ดังนั้นคำถามที่เกิดขึ้นคือความแตกต่างระหว่างอาเรย์และรายการที่เชื่อมโยงคืออะไรและเมื่อใดควรใช้อาเรย์แทนที่จะเป็นรายการที่เชื่อมโยงกัน อาจเป็นสองปัจจัยที่สำคัญที่สุดในการตัดสินใจที่จะใช้ของคุณ:

  • รายการที่เชื่อมโยงมีประสิทธิภาพในการแทรก / กำจัดที่ดีขึ้นมากตราบใดที่การแทรก / การลบไม่ได้อยู่ในองค์ประกอบสุดท้ายในคอลเลกชัน นี่เป็นเพราะอาร์เรย์จะต้องเลื่อนองค์ประกอบที่เหลือทั้งหมดที่อยู่หลังจุดแทรก / ลบ หากการแทรก / การลบอยู่ที่ส่วนท้ายของรายการอย่างไรก็ตามไม่จำเป็นต้องทำการเปลี่ยนแปลงนี้ (แม้ว่าอาเรย์อาจจำเป็นต้องปรับขนาดถ้าความจุของมันเกิน)
  • อาร์เรย์มีความสามารถในการเข้าถึงที่ดีขึ้นมาก อาร์เรย์สามารถทำดัชนีโดยตรง (ในเวลาคงที่) รายการที่เชื่อมโยงจะต้องถูกสำรวจ (เวลาเชิงเส้น)

1

สิ่งนี้ดัดแปลงมาจากTono Namคำตอบที่ยอมรับของแก้ไขการวัดที่ผิดเล็กน้อย

การทดสอบ:

static void Main()
{
    LinkedListPerformance.AddFirst_List(); // 12028 ms
    LinkedListPerformance.AddFirst_LinkedList(); // 33 ms

    LinkedListPerformance.AddLast_List(); // 33 ms
    LinkedListPerformance.AddLast_LinkedList(); // 32 ms

    LinkedListPerformance.Enumerate_List(); // 1.08 ms
    LinkedListPerformance.Enumerate_LinkedList(); // 3.4 ms

    //I tried below as fun exercise - not very meaningful, see code
    //sort of equivalent to insertion when having the reference to middle node

    LinkedListPerformance.AddMiddle_List(); // 5724 ms
    LinkedListPerformance.AddMiddle_LinkedList1(); // 36 ms
    LinkedListPerformance.AddMiddle_LinkedList2(); // 32 ms
    LinkedListPerformance.AddMiddle_LinkedList3(); // 454 ms

    Environment.Exit(-1);
}

และรหัส:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace stackoverflow
{
    static class LinkedListPerformance
    {
        class Temp
        {
            public decimal A, B, C, D;

            public Temp(decimal a, decimal b, decimal c, decimal d)
            {
                A = a; B = b; C = c; D = d;
            }
        }



        static readonly int start = 0;
        static readonly int end = 123456;
        static readonly IEnumerable<Temp> query = Enumerable.Range(start, end - start).Select(temp);

        static Temp temp(int i)
        {
            return new Temp(i, i, i, i);
        }

        static void StopAndPrint(this Stopwatch watch)
        {
            watch.Stop();
            Console.WriteLine(watch.Elapsed.TotalMilliseconds);
        }

        public static void AddFirst_List()
        {
            var list = new List<Temp>();
            var watch = Stopwatch.StartNew();

            for (var i = start; i < end; i++)
                list.Insert(0, temp(i));

            watch.StopAndPrint();
        }

        public static void AddFirst_LinkedList()
        {
            var list = new LinkedList<Temp>();
            var watch = Stopwatch.StartNew();

            for (int i = start; i < end; i++)
                list.AddFirst(temp(i));

            watch.StopAndPrint();
        }

        public static void AddLast_List()
        {
            var list = new List<Temp>();
            var watch = Stopwatch.StartNew();

            for (var i = start; i < end; i++)
                list.Add(temp(i));

            watch.StopAndPrint();
        }

        public static void AddLast_LinkedList()
        {
            var list = new LinkedList<Temp>();
            var watch = Stopwatch.StartNew();

            for (int i = start; i < end; i++)
                list.AddLast(temp(i));

            watch.StopAndPrint();
        }

        public static void Enumerate_List()
        {
            var list = new List<Temp>(query);
            var watch = Stopwatch.StartNew();

            foreach (var item in list)
            {

            }

            watch.StopAndPrint();
        }

        public static void Enumerate_LinkedList()
        {
            var list = new LinkedList<Temp>(query);
            var watch = Stopwatch.StartNew();

            foreach (var item in list)
            {

            }

            watch.StopAndPrint();
        }

        //for the fun of it, I tried to time inserting to the middle of 
        //linked list - this is by no means a realistic scenario! or may be 
        //these make sense if you assume you have the reference to middle node

        //insertion to the middle of list
        public static void AddMiddle_List()
        {
            var list = new List<Temp>();
            var watch = Stopwatch.StartNew();

            for (var i = start; i < end; i++)
                list.Insert(list.Count / 2, temp(i));

            watch.StopAndPrint();
        }

        //insertion in linked list in such a fashion that 
        //it has the same effect as inserting into the middle of list
        public static void AddMiddle_LinkedList1()
        {
            var list = new LinkedList<Temp>();
            var watch = Stopwatch.StartNew();

            LinkedListNode<Temp> evenNode = null, oddNode = null;
            for (int i = start; i < end; i++)
            {
                if (list.Count == 0)
                    oddNode = evenNode = list.AddLast(temp(i));
                else
                    if (list.Count % 2 == 1)
                        oddNode = list.AddBefore(evenNode, temp(i));
                    else
                        evenNode = list.AddAfter(oddNode, temp(i));
            }

            watch.StopAndPrint();
        }

        //another hacky way
        public static void AddMiddle_LinkedList2()
        {
            var list = new LinkedList<Temp>();
            var watch = Stopwatch.StartNew();

            for (var i = start + 1; i < end; i += 2)
                list.AddLast(temp(i));
            for (int i = end - 2; i >= 0; i -= 2)
                list.AddLast(temp(i));

            watch.StopAndPrint();
        }

        //OP's original more sensible approach, but I tried to filter out
        //the intermediate iteration cost in finding the middle node.
        public static void AddMiddle_LinkedList3()
        {
            var list = new LinkedList<Temp>();
            var watch = Stopwatch.StartNew();

            for (var i = start; i < end; i++)
            {
                if (list.Count == 0)
                    list.AddLast(temp(i));
                else
                {
                    watch.Stop();
                    var curNode = list.First;
                    for (var j = 0; j < list.Count / 2; j++)
                        curNode = curNode.Next;
                    watch.Start();

                    list.AddBefore(curNode, temp(i));
                }
            }

            watch.StopAndPrint();
        }
    }
}

คุณสามารถดูผลลัพธ์ที่เป็นไปตามประสิทธิภาพทางทฤษฎีที่คนอื่นมีเอกสารไว้ที่นี่ ค่อนข้างชัดเจน - LinkedList<T>ได้รับเวลามากในกรณีที่มีการแทรก ฉันไม่ได้ทดสอบเพื่อลบออกจากรายการกลาง แต่ผลลัพธ์ควรจะเหมือนกัน แน่นอนว่าList<T>มีพื้นที่อื่น ๆ ที่มันทำงานได้ดีกว่าเช่นการเข้าถึงแบบสุ่ม O (1)


0

ใช้LinkedList<>เมื่อ

  1. คุณไม่รู้ว่ามีวัตถุกี่ชิ้นที่ผ่านเข้ามาทางประตูน้ำท่วม ตัวอย่างเช่นToken Stream.
  2. เมื่อคุณต้องการลบ \ insert ที่ส่วนท้ายเท่านั้น

List<>สำหรับทุกอย่างอื่นจะดีกว่าการใช้งาน


6
ฉันไม่เห็นว่าทำไมจุดที่ 2 สมเหตุสมผล รายการที่เชื่อมโยงนั้นยอดเยี่ยมเมื่อคุณทำการแทรก / ลบหลายรายการตลอดทั้งรายการ
Drew Noakes

เนื่องจากข้อเท็จจริงที่ว่า LinkedLists ไม่ใช่ดัชนีคุณจึงต้องสแกนรายการทั้งหมดเพื่อแทรกหรือลบที่ได้รับโทษ O (n) รายการ <> ในทางกลับกันการปรับขนาดอาร์เรย์ แต่ยังคง IMO เป็นตัวเลือกที่ดีกว่าเมื่อเทียบกับ LinkedLists
Antony Thomas

1
คุณไม่ต้องสแกนรายการสำหรับการแทรก / ลบหากคุณติดตามLinkedListNode<T>วัตถุในรหัสของคุณ หากคุณสามารถทำสิ่งนั้นได้ดีกว่าการใช้List<T>โดยเฉพาะอย่างยิ่งสำหรับรายการที่ยาวมากซึ่งมีการแทรก / ลบบ่อยครั้ง
Drew Noakes

คุณหมายถึงผ่าน hashtable หรือไม่? หากเป็นเช่นนั้นจะเป็นพื้นที่ \ time โดยทั่วไปที่โปรแกรมเมอร์คอมพิวเตอร์ทุกคนควรเลือกตามโดเมนปัญหา :) แต่ใช่นั่นจะทำให้เร็วขึ้น
แอนโทนีโทมัส

1
@AntonyThomas - ไม่เขาหมายถึงการส่งผ่านการอ้างอิงไปยังโหนดแทนที่จะส่งผ่านการอ้างอิงไปยังองค์ประกอบต่างๆ หากสิ่งที่คุณต้องเป็นองค์ประกอบแล้วทั้งรายการและ LinkedList มีประสิทธิภาพการทำงานที่ไม่ดีเพราะคุณจะต้องค้นหา หากคุณคิดว่า "แต่มีรายการฉันสามารถส่งผ่านดัชนี": ที่ถูกต้องเฉพาะเมื่อคุณไม่เคยใส่องค์ประกอบใหม่เข้าไปในกลางของรายการ LinkedList ไม่มีข้อ จำกัด นี้หากคุณยึดโหนดไว้ (และใช้node.Valueเมื่อใดก็ตามที่คุณต้องการองค์ประกอบดั้งเดิม) ดังนั้นคุณเขียนอัลกอริธึมใหม่เพื่อทำงานกับโหนดไม่ใช่ค่าดิบ
ToolmakerSteve

0

ฉันเห็นด้วยกับประเด็นส่วนใหญ่ที่กล่าวข้างต้น และฉันก็เห็นด้วยว่ารายการดูเหมือนจะเป็นตัวเลือกที่ชัดเจนกว่าในกรณีส่วนใหญ่

แต่ฉันแค่ต้องการเพิ่มว่ามีหลายตัวอย่างที่ LinkedList เป็นตัวเลือกที่ดีกว่า List เพื่อประสิทธิภาพที่ดีกว่า

  1. สมมติว่าคุณกำลังสำรวจองค์ประกอบและคุณต้องการแทรก / ลบจำนวนมาก LinkedList ทำในเวลาเชิงเส้น O (n) ในขณะที่ List ทำในเวลากำลังสอง O (n ^ 2)
  2. สมมติว่าคุณต้องการเข้าถึงวัตถุที่มีขนาดใหญ่ขึ้นเรื่อย ๆ อีกครั้ง LinkedList มีประโยชน์มากขึ้น
  3. Deque () และ Queue () จะถูกนำไปใช้งานได้ดีขึ้นโดยใช้ LinkedList
  4. การเพิ่มขนาดของ LinkedList นั้นง่ายและดีขึ้นมากเมื่อคุณจัดการกับวัตถุจำนวนมากและใหญ่กว่า

หวังว่าบางคนจะเห็นความคิดเห็นเหล่านี้มีประโยชน์


โปรดทราบว่าคำแนะนำนี้ใช้สำหรับ. NET ไม่ใช่ Java ในการใช้งานลิสต์ที่เชื่อมโยงของ Java คุณไม่มีแนวคิดของ "โหนดปัจจุบัน" ดังนั้นคุณต้องสำรวจรายการสำหรับแต่ละส่วนแทรก
Jonathan Allen

คำตอบนี้ถูกต้องเพียงบางส่วนเท่านั้น: 2) ถ้าองค์ประกอบมีขนาดใหญ่แล้วทำให้องค์ประกอบประเภท Class ไม่ได้เป็นโครงสร้างเพื่อให้รายการนั้นมีการอ้างอิง จากนั้นขนาดองค์ประกอบจะไม่เกี่ยวข้อง 3) Deque และคิวสามารถทำได้อย่างมีประสิทธิภาพในรายการถ้าคุณใช้ List เป็น "Circular buffer" แทนที่จะทำการแทรกหรือลบเมื่อเริ่มต้น StephenCleary ของ Deque 4) ความจริงบางส่วน: เมื่อวัตถุหลายตัวโปรของ LL ไม่ต้องการหน่วยความจำขนาดใหญ่ที่ต่อเนื่องกัน ข้อเสียคือหน่วยความจำเพิ่มเติมสำหรับตัวชี้โหนด
ToolmakerSteve

-2

คำตอบโดยเฉลี่ยมากมายที่นี่ ...

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

ใช้รายการที่ลิงก์เมื่อ

1) คุณต้องการความปลอดภัยของเธรด คุณสามารถสร้าง algos safe thread ได้ดีขึ้น ค่าใช้จ่ายในการล็อคจะครอบงำรายการสไตล์ที่เกิดขึ้นพร้อมกัน

2) หากคุณมีคิวขนาดใหญ่เช่นโครงสร้างและต้องการลบหรือเพิ่มที่ใดก็ได้ แต่จบลงตลอดเวลา > 100K รายการมีอยู่จริง แต่ไม่ใช่ว่าเป็นเรื่องปกติ


3
คำถามนี้เกี่ยวกับการใช้งาน C # สองรายการไม่ใช่รายการที่เชื่อมโยงโดยทั่วไป
Jonathan Allen

เหมือนกันในทุกภาษา
user1496062

-2

ฉันถามคำถามที่คล้ายกันที่เกี่ยวข้องกับประสิทธิภาพของการรวบรวม LinkedListและค้นพบการใช้ C # ของ Steven Cleary ใน Dequeเป็นวิธีแก้ปัญหา ซึ่งแตกต่างจากคอลเลกชันคิว Deque ช่วยให้การย้ายรายการเปิด / ปิดด้านหน้าและด้านหลัง มันคล้ายกับรายการที่เชื่อมโยง แต่มีการปรับปรุงประสิทธิภาพ


1
Re คำสั่งของคุณที่Dequeเป็น"คล้ายกับรายการที่เชื่อมโยง แต่มีประสิทธิภาพการทำงานที่ดีขึ้น" โปรดมีสิทธิ์คำว่า: Dequeคือประสิทธิภาพที่ดีกว่าLinkedList, รหัสเฉพาะของคุณ ตามลิงค์ของคุณฉันเห็นว่าอีกสองวันต่อมาคุณเรียนรู้จาก Ivan Stoev ว่านี่ไม่ใช่การเชื่อมโยงที่ไม่มีประสิทธิภาพ แต่เป็นความไร้ประสิทธิภาพในรหัสของคุณ (และถึงแม้ว่ามันจะไม่มีประสิทธิภาพของ LinkedList ที่จะไม่ปรับงบทั่วไปว่า Deque มีประสิทธิภาพมากขึ้นเฉพาะในบางกรณีเท่านั้น)
ToolmakerSteve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.