วิธีการเติม / สร้างอินสแตนซ์ของอาร์เรย์ C # ด้วยค่าเดียวได้อย่างไร


205

ฉันรู้ว่าอาร์เรย์ประเภทอินสแตนซ์ของค่าใน C # นั้นมีค่าเริ่มต้นโดยอัตโนมัติพร้อมกับค่าเริ่มต้นของประเภท (เช่น false สำหรับ bool, 0 สำหรับ int ฯลฯ )

มีวิธีการเติมข้อมูลอาร์เรย์โดยอัตโนมัติด้วยค่าเมล็ดที่ไม่ใช่ค่าเริ่มต้นหรือไม่ ทั้งในการสร้างหรือวิธีการในตัวหลังจากนั้น (เช่นArrays.fillของ Java () )? ว่าฉันต้องการอาร์เรย์แบบบูลที่เป็นจริงโดยค่าเริ่มต้นแทนที่จะเป็นเท็จ มีวิธีการในการทำเช่นนี้หรือคุณเพียงแค่ต้องทำซ้ำผ่านอาร์เรย์กับห่วงสำหรับ?

 // Example pseudo-code:
 bool[] abValues = new[1000000];
 Array.Populate(abValues, true);

 // Currently how I'm handling this:
 bool[] abValues = new[1000000];
 for (int i = 0; i < 1000000; i++)
 {
     abValues[i] = true;
 }

การวนซ้ำผ่านอาร์เรย์และ "รีเซ็ต" แต่ละค่าเป็นจริงดูเหมือนไม่มีประสิทธิภาพ อย่างไรก็ตามมีรอบนี้หรือไม่ อาจจะพลิกค่าทั้งหมดหรือไม่

หลังจากพิมพ์คำถามนี้และคิดเกี่ยวกับมันฉันเดาว่าค่าเริ่มต้นนั้นเป็นเพียงผลมาจากวิธีการที่ C # จัดการการจัดสรรหน่วยความจำของวัตถุเหล่านี้อยู่เบื้องหลังดังนั้นฉันคิดว่ามันอาจเป็นไปไม่ได้ แต่ฉันก็ยังอยากรู้แน่นอน!


ฉันมักจะเปลี่ยนชื่อจาก is_found เป็น is_still_hiding รักคำตอบแม้ว่าฉันต้องทำเหมือนกันสำหรับอาร์เรย์ของ int ในกรณีทดสอบ (คำถามที่ดี)
ctrl-alt-delor

คำตอบ:


146

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

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}

3
ต้องการ ++ i แทน i ++ หากคุณไม่ต้องการสำเนา
void.pointer

24
i ++ คัดลอก i, เพิ่มขึ้น i, และส่งคืนค่าดั้งเดิม ++ ฉันแค่ส่งคืนค่าที่เพิ่มขึ้น ดังนั้น ++ ฉันจึงเร็วขึ้นซึ่งอาจมีความหมายในลูปขนาดใหญ่เหมือนที่เรากำลังพูดถึงที่นี่
tenpn

57
@RobertDailey: นั่นคือการเพิ่มประสิทธิภาพของคอมไพเลอร์และไม่เป็นความจริงอีกต่อไป ฉันเพิ่งทดสอบเพื่อยืนยันความเชื่อของฉัน: หากค่าส่งคืนของ i ++ ไม่ได้ถูกใช้กับอะไรก็ตามคอมไพเลอร์จะรวบรวมมันเป็น ++ i โดยอัตโนมัติสำหรับคุณ นอกจากนี้แม้ว่าฉันจะใช้ค่าที่ส่งคืน แต่ความแตกต่างของประสิทธิภาพก็เล็กมากฉันก็ต้องสร้างเคสให้สุดขีดเพื่อวัดมัน ถึงอย่างนั้นมันก็ส่งผลให้รันไทม์ที่แตกต่างกันเพียงไม่กี่เปอร์เซ็นต์
Edward Ned Harvey

8
ผมเขียนวิธีการขยายเช่นนี้ แต่ฉันมีมันกลับอาร์เรย์เดิมเพื่อให้วิธีการผูกมัดเช่น:int[] arr = new int[16].Populate(-1);
Gutblender

2
เปลี่ยนvoidเป็นT[]และจากนั้นคุณสามารถทำได้var a = new int[100].Polupate(1)
orad

198
Enumerable.Repeat(true, 1000000).ToArray();

70
ขณะนี้ใช้งานได้มันไม่ได้เป็นทางออกที่ดีเพราะมันช้ามาก มันช้ากว่าการวนซ้ำประมาณ 4 เท่าโดยแท้จริงแล้วสำหรับลูป
patjbs

4
ใช่ว่าเป็นความจริงเมื่อเราพิจารณาผลการดำเนินงานสำหรับวงเร็ว
Rony

6
จะเห็นบางส่วนมาตรฐานที่แท้จริงมีลักษณะที่C # เริ่มต้นอาร์เรย์
theknut

4
Enumerable.ToArrayไม่ทราบขนาดของลำดับที่นับได้ดังนั้นจึงต้องเดาว่ามีขนาดอาร์เรย์เท่าใด นั่นหมายความว่าคุณจะได้รับการจัดสรรอาเรย์ทุกครั้งToArrayที่บัฟเฟอร์มีค่าเกินรวมถึงการจัดสรรอีกหนึ่งครั้งที่ส่วนท้ายของการตัดแต่ง นอกจากนี้ยังมีค่าใช้จ่ายที่เกี่ยวข้องกับวัตถุที่นับได้
Edward Brey

5
เพียงแค่ทราบว่าด้วยประเภทการอ้างอิงนี้จะเติมทั้งอาร์เรย์ที่มีการอ้างอิงทั้งหมดไปยังวัตถุเดียวที่เหมือนกัน ถ้าไม่ได้เป็นสิ่งที่คุณต้องการและคุณต้องการจริงในการสร้างวัตถุที่แตกต่างกันสำหรับรายการอาร์เรย์แต่ละดูstackoverflow.com/a/44937053/23715
Alex Che

74

สร้างอาร์เรย์ใหม่ด้วยtrueค่าพัน:

var items = Enumerable.Repeat<bool>(true, 1000).ToArray();  // Or ToList(), etc.

ในทำนองเดียวกันคุณสามารถสร้างลำดับจำนวนเต็ม:

var items = Enumerable.Range(0, 1000).ToArray();  // 0..999

8
ไม่ได้เลวร้าย แต่ก็ยังคงช้ากว่าสำหรับวงประมาณปัจจัย 4x
patjbs

1
patjbs ในทางทฤษฎีในอนาคตนับซ้ำทำซ้ำจะทำงานได้เร็วขึ้นเพราะจะใช้การดำเนินการแบบขนาน
Petar Petrov

1
@PetarPetrov สิ่งนี้จะไม่เกิดขึ้นเนื่องจากการจู่โจมแคช ฉันค่อนข้างแน่ใจว่าเนื่องจากลักษณะของแคช CPU การทำงานแบบขนานบนอาเรย์เดียวจะช้าลงเสมอไม่ว่าจะเกิดอะไรขึ้นเนื่องจากคอมพิวเตอร์คาดว่าจะทำงานแบบซิงโครนัสและโหลดข้อมูลอย่างเหมาะสม
TernaryTopiary

เจตนาในแง่ร้าย! = ขาดการเพิ่มประสิทธิภาพก่อนวัยอันควร
เดนิส Gladkiy

24

สำหรับอาร์เรย์ขนาดใหญ่หรืออาร์เรย์ที่มีขนาดตัวแปรคุณควรใช้:

Enumerable.Repeat(true, 1000000).ToArray();

สำหรับอาร์เรย์ขนาดเล็กคุณสามารถใช้ไวยากรณ์การเริ่มต้นการรวบรวมใน C # 3:

bool[] vals = new bool[]{ false, false, false, false, false, false, false };

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

bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };

และเพื่อเริ่มต้นอาร์เรย์ลอย []:float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
Jim Lahman

การกำหนดค่าเริ่มต้นของ FWIW ของอาร์เรย์สามารถทำได้ในรุ่น C # ใด ๆ เช่น:bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Peter van der Heijden

24

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

System.Collections.BitArray falses = new System.Collections.BitArray(100000, false);
System.Collections.BitArray trues = new System.Collections.BitArray(100000, true);

// Now both contain only true values.
falses.And(trues);

17

คุณสามารถใช้Array.Fillใน. NET Core 2.0+ และ. NET Standard 2.1+


ยอดเยี่ยม แม้ว่าจะทราบว่ามันเป็นวิธีการที่ค่อนข้างใหม่ มันมีอยู่ใน. NET Core 2.0+ และ. NET Standard 2.1 แต่โดยเฉพาะไม่ได้อยู่ใน. NET Framework เวอร์ชันใด ๆ (จะอยู่ใน. NET 5.0 ซึ่งผสาน. NET Framework และ. NET Core เข้าด้วยกัน)
อาเบล

9

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

class Program
{
    static void Main(string[] args)
    {
        int[] arr = new int[1000];
        arr.Init(10);
        Array.ForEach(arr, Console.WriteLine);
    }
}

public static class ArrayExtensions
{
    public static void Init<T>(this T[] array, T defaultVaue)
    {
        if (array == null)
            return;
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = defaultVaue;
        }
    }
}

ฉันชอบความคิดของส่วนขยายยิ่งขุดเข้าไปในนี้ บางครั้งการแก้ปัญหาที่ง่ายและตรงไปตรงมานั้นดีที่สุด!
patjbs

8

หลังจาก googling และการอ่านอีกเล็กน้อยฉันพบสิ่งนี้:

bool[] bPrimes = new bool[1000000];
bPrimes = Array.ConvertAll<bool, bool>(bPrimes, b=> b=true);

ซึ่งใกล้กับสิ่งที่ฉันกำลังมองหาอย่างแน่นอน แต่ฉันไม่แน่ใจว่ามันดีกว่าการวนซ้ำในอาร์เรย์เดิมใน for-loop และเพียงแค่เปลี่ยนค่า หลังจากการทดสอบอย่างรวดเร็วในความเป็นจริงมันจะช้าลงประมาณ 5 เท่าดังนั้นจึงไม่ใช่ทางออกที่ดีจริงๆ!


4
มันคล้ายกับสิ่งที่คุณพยายามทำยกเว้นการเรียกฟังก์ชันสำหรับแต่ละองค์ประกอบในอาเรย์ของคุณ มันอาจจะดูมาก nicer syntactically แต่การทำของการทำงานมากขึ้น ...
Nader Shirazie

ใช่มันดูเหมือนว่าเพียงแค่สำหรับวงวนทำหน้าที่เช่นเดียวกับสิ่งอื่นใด
4322 patjbs

มันสร้างอาร์เรย์ใหม่ (ไม่เปลี่ยนอินสแตนซ์ดั้งเดิม)
Jeppe Stig Nielsen

7

สิ่งที่เกี่ยวกับการใช้งานแบบขนาน

public static void InitializeArray<T>(T[] array, T value)
{
    var cores = Environment.ProcessorCount;

    ArraySegment<T>[] segments = new ArraySegment<T>[cores];

    var step = array.Length / cores;
    for (int i = 0; i < cores; i++)
    {
        segments[i] = new ArraySegment<T>(array, i * step, step);
    }
    var remaining = array.Length % cores;
    if (remaining != 0)
    {
        var lastIndex = segments.Length - 1;
        segments[lastIndex] = new ArraySegment<T>(array, lastIndex * step, array.Length - (lastIndex * step));
    }

    var initializers = new Task[cores];
    for (int i = 0; i < cores; i++)
    {
        var index = i;
        var t = new Task(() =>
        {
            var s = segments[index];
            for (int j = 0; j < s.Count; j++)
            {
                array[j + s.Offset] = value;
            }
        });
        initializers[i] = t;
        t.Start();
    }

    Task.WaitAll(initializers);
}

เมื่อเริ่มต้นอาร์เรย์เท่านั้นพลังของรหัสนี้ไม่สามารถมองเห็นได้ แต่ฉันคิดว่าคุณควรลืมเรื่อง "บริสุทธิ์" อย่างแน่นอน


สิ่งนี้มีความเสี่ยงต่อปัญหาของการแบ่งปันที่ผิดพลาดซึ่งเธรดต่างกันแข่งขันกันสำหรับแคชแคชของ CPU และลดประสิทธิภาพเมื่อเทียบกับการใช้งานแบบเธรดเดี่ยว ขึ้นอยู่กับขนาดของบล็อกหน่วยความจำต่อเธรดและสถาปัตยกรรมของ CPU
Eric J.

7

รหัสด้านล่างรวมการทำซ้ำง่าย ๆ สำหรับสำเนาขนาดเล็กและ Array.Copy สำหรับสำเนาขนาดใหญ่

    public static void Populate<T>( T[] array, int startIndex, int count, T value ) {
        if ( array == null ) {
            throw new ArgumentNullException( "array" );
        }
        if ( (uint)startIndex >= array.Length ) {
            throw new ArgumentOutOfRangeException( "startIndex", "" );
        }
        if ( count < 0 || ( (uint)( startIndex + count ) > array.Length ) ) {
            throw new ArgumentOutOfRangeException( "count", "" );
        }
        const int Gap = 16;
        int i = startIndex;

        if ( count <= Gap * 2 ) {
            while ( count > 0 ) {
                array[ i ] = value;
                count--;
                i++;
            }
            return;
        }
        int aval = Gap;
        count -= Gap;

        do {
            array[ i ] = value;
            i++;
            --aval;
        } while ( aval > 0 );

        aval = Gap;
        while ( true ) {
            Array.Copy( array, startIndex, array, i, aval );
            i += aval;
            count -= aval;
            aval *= 2;
            if ( count <= aval ) {
                Array.Copy( array, startIndex, array, i, count );
                break;
            }
        }
    }

มาตรฐานสำหรับความยาวของอาร์เรย์ที่แตกต่างกันโดยใช้อาร์เรย์ [] คือ:

         2 Iterate:     1981 Populate:     2845
         4 Iterate:     2678 Populate:     3915
         8 Iterate:     4026 Populate:     6592
        16 Iterate:     6825 Populate:    10269
        32 Iterate:    16766 Populate:    18786
        64 Iterate:    27120 Populate:    35187
       128 Iterate:    49769 Populate:    53133
       256 Iterate:   100099 Populate:    71709
       512 Iterate:   184722 Populate:   107933
      1024 Iterate:   363727 Populate:   126389
      2048 Iterate:   710963 Populate:   220152
      4096 Iterate:  1419732 Populate:   291860
      8192 Iterate:  2854372 Populate:   685834
     16384 Iterate:  5703108 Populate:  1444185
     32768 Iterate: 11396999 Populate:  3210109

คอลัมน์แรกคือขนาดอาร์เรย์ตามด้วยเวลาของการคัดลอกโดยใช้การวนซ้ำแบบง่าย (การใช้งาน @JaredPared) เวลาของวิธีนี้คือหลังจากนั้น สิ่งเหล่านี้เป็นเกณฑ์มาตรฐานโดยใช้อาร์เรย์ของโครงสร้างของจำนวนเต็มสี่จำนวน

         2 Iterate:     2473 Populate:     4589
         4 Iterate:     3966 Populate:     6081
         8 Iterate:     7326 Populate:     9050
        16 Iterate:    14606 Populate:    16114
        32 Iterate:    29170 Populate:    31473
        64 Iterate:    57117 Populate:    52079
       128 Iterate:   112927 Populate:    75503
       256 Iterate:   226767 Populate:   133276
       512 Iterate:   447424 Populate:   165912
      1024 Iterate:   890158 Populate:   367087
      2048 Iterate:  1786918 Populate:   492909
      4096 Iterate:  3570919 Populate:  1623861
      8192 Iterate:  7136554 Populate:  2857678
     16384 Iterate: 14258354 Populate:  6437759
     32768 Iterate: 28351852 Populate: 12843259

7

หรือ ... คุณสามารถใช้ตรรกะกลับด้านได้ ขอfalseค่าเฉลี่ยtrueและกลับกัน

ตัวอย่างโค้ด

// bool[] isVisible = Enumerable.Repeat(true, 1000000).ToArray();
bool[] isHidden = new bool[1000000]; // Crazy-fast initialization!

// if (isVisible.All(v => v))
if (isHidden.All(v => !v))
{
    // Do stuff!
}

วิธีแก้ปัญหาตลกแม้ว่าทั้งหมดนี้จะยากขึ้นด้วยเช่น ints เพราะคุณสูญเสีย 0
MrFox

1
นี่เป็นตัวเลือกที่ทำงานได้จริงถ้าคุณ "สลับตรรกะ" ในชื่อตัวแปร: แทนที่จะbool[] isVisibleทำมันbool[] isHidden
Markus Hütter

1
ผู้คนดูเหมือนจะตอบโต้เช่นนี้เป็นการแฮ็กตลกบางประเภท มันเป็นเทคนิคการเพิ่มประสิทธิภาพทั่วไป หากคุณโชคดีผู้แปลจะทำสิ่งนี้ให้คุณ
l33t


4

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

.Net Standard 2.1 (ในตัวอย่างก่อนหน้าของการเขียนนี้) ให้Array.Fill ()ซึ่งให้ยืมตัวเองเพื่อการใช้งานที่มีประสิทธิภาพสูงในไลบรารีรันไทม์ (แต่ ณ ตอนนี้. NET Core ดูเหมือนจะไม่ใช้ประโยชน์จากความเป็นไปได้นั้น) .

สำหรับผู้ที่อยู่บนแพลตฟอร์มก่อนหน้านี้วิธีการขยายต่อไปนี้มีประสิทธิภาพมากกว่าการวนรอบเล็กน้อยโดยมีระยะขอบที่มากเมื่อขนาดอาร์เรย์มีความสำคัญ ฉันสร้างมันขึ้นมาเมื่อโซลูชันของฉันสำหรับการท้าทายรหัสออนไลน์อยู่ที่ประมาณ 20% ของงบประมาณเวลาที่จัดสรร มันลดรันไทม์ลงประมาณ 70% ในกรณีนี้การเติมอาร์เรย์จะดำเนินการภายในวงอื่น BLOCK_SIZE ถูกกำหนดโดยความรู้สึกของลำไส้มากกว่าการทดสอบ การปรับให้เหมาะสมบางอย่างเป็นไปได้ (เช่นการคัดลอกไบต์ทั้งหมดที่ตั้งค่าเป็นค่าที่ต้องการมากกว่าบล็อกขนาดคงที่)

internal const int BLOCK_SIZE = 256;
public static void Fill<T>(this T[] array, T value)
{
    if (array.Length < 2 * BLOCK_SIZE)
    {
        for (int i = 0; i < array.Length; i++) array[i] = value;
    }
    else
    {
        int fullBlocks = array.Length / BLOCK_SIZE;
        // Initialize first block
        for (int j = 0; j < BLOCK_SIZE; j++) array[j] = value;
        // Copy successive full blocks
        for (int blk = 1; blk < fullBlocks; blk++)
        {
            Array.Copy(array, 0, array, blk * BLOCK_SIZE, BLOCK_SIZE);
        }

        for (int rem = fullBlocks * BLOCK_SIZE; rem < array.Length; rem++)
        {
            array[rem] = value;
        }
    }
}

3

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

public class SparseArray<T>
{
    private Dictionary<int, T> values = new Dictionary<int, T>();

    private T defaultValue;

    public SparseArray(T defaultValue)
    {
        this.defaultValue = defaultValue;
    }

    public T this [int index]
    {
      set { values[index] = value; }
      get { return values.ContainsKey(index) ? values[index] ? defaultValue; }
    }
}

คุณอาจจะต้องใช้การเชื่อมต่ออื่น ๆ ที่จะทำให้มันมีประโยชน์เช่นผู้ที่อยู่ในอาร์เรย์ตัวเอง


3

ไม่มีวิธีการตั้งค่าองค์ประกอบทั้งหมดในอาร์เรย์เป็นการดำเนินการเดียว UNLESS ค่านั้นเป็นค่าเริ่มต้นประเภทองค์ประกอบ

เช่นถ้าเป็นอาร์เรย์ของจำนวนเต็มคุณสามารถตั้งค่าทั้งหมดให้เป็นศูนย์ด้วยการดำเนินการเดียวเช่น: Array.Clear(...)


2

ฉันรู้ว่าฉันมาปาร์ตี้ช้า แต่นี่เป็นความคิด เขียน wrapper ที่มีโอเปอเรเตอร์การแปลงไปยังและจากค่าที่ห่อเพื่อให้สามารถใช้เป็นสแตนด์อะโลนสำหรับชนิดที่ถูกห่อ นี่เป็นแรงบันดาลใจจริง ๆ จากคำตอบที่ฟังดูโง่จาก @ l33t

ก่อน (มาจาก C ++) ฉันรู้ว่าใน C # ctor เริ่มต้นจะไม่ถูกเรียกเมื่อองค์ประกอบของอาร์เรย์ถูกสร้างขึ้น แทน - แม้ในที่ที่มีคอนสตรัคเตอร์เริ่มต้นที่ผู้ใช้กำหนดเอง! - องค์ประกอบอาเรย์ทั้งหมดไม่มีการเตรียมใช้งาน นั่นทำให้ฉันประหลาดใจ

ดังนั้นคลาส wrapper ซึ่งให้ ctor เริ่มต้นด้วยค่าที่ต้องการจะใช้กับอาร์เรย์ใน C ++ แต่ไม่ใช่ใน C # วิธีแก้ไขคือให้แผนที่ชนิด wrapper 0 เป็นค่าเมล็ดที่ต้องการเมื่อทำการแปลง ด้วยวิธีการนั้นค่าเริ่มต้นที่เป็นศูนย์จะปรากฏขึ้นพร้อมกับเมล็ดเพื่อวัตถุประสงค์ในทางปฏิบัติทั้งหมด:

public struct MyBool
{
    private bool _invertedValue;

    public MyBool(bool b) 
    {   
        _invertedValue = !b;
    }

    public static implicit operator MyBool(bool b)
    {
        return new MyBool(b);
    }

    public static implicit operator bool(MyBool mb)
    {
        return !mb._invertedValue;
    }

}

static void Main(string[] args)
{
        MyBool mb = false; // should expose false.
        Console.Out.WriteLine("false init gives false: " 
                              + !mb);

        MyBool[] fakeBoolArray = new MyBool[100];

        Console.Out.WriteLine("Default array elems are true: " 
                              + fakeBoolArray.All(b => b) );

        fakeBoolArray[21] = false;
        Console.Out.WriteLine("Assigning false worked: " 
                              + !fakeBoolArray[21]);

        fakeBoolArray[21] = true;
        // Should define ToString() on a MyBool,
        // hence the !! to force bool
        Console.Out.WriteLine("Assigning true again worked: " 
                              + !!fakeBoolArray[21]);
}

รูปแบบนี้ใช้ได้กับทุกประเภทค่า หนึ่งสามารถทำได้เช่นแผนที่ 0 ถึง 4 สำหรับ ints หากต้องการเริ่มต้นด้วย 4 เป็นที่ต้องการ ฯลฯ

ฉันชอบที่จะสร้างแม่แบบของมันเป็นไปได้ใน C ++ โดยให้ค่าเมล็ดเป็นพารามิเตอร์แม่แบบ แต่ฉันเข้าใจว่ามันเป็นไปไม่ได้ใน C # หรือว่าฉันขาดอะไรไป? (แน่นอนว่าในการทำแผนที่ C ++ นั้นไม่จำเป็นเลยเพราะสามารถให้ ctor เริ่มต้นซึ่งจะถูกเรียกสำหรับองค์ประกอบอาร์เรย์)

FWIW นี่เป็น c ++ เทียบเท่า: https://ideone.com/wG8yEh


2

หากคุณสามารถสลับตรรกะของคุณคุณสามารถใช้Array.Clear()วิธีการตั้งค่าอาร์เรย์บูลีนเป็นเท็จ

        int upperLimit = 21;
        double optimizeMe = Math.Sqrt(upperLimit);

        bool[] seiveContainer = new bool[upperLimit];
        Array.Clear(seiveContainer, 0, upperLimit);

2

ถ้าคุณอยู่บน. NET Core,. NET Standard> = 2.1 หรือขึ้นอยู่กับแพคเกจ System.Memory คุณยังสามารถใช้Span<T>.Fill()วิธีนี้ได้

var valueToFill = 165;
var data = new int[100];

data.AsSpan().Fill(valueToFill);

// print array content
for (int i = 0; i < data.Length; i++)
{
    Console.WriteLine(data[i]);
}

https://dotnetfiddle.net/UsJ9bu


2

นี่เป็นอีกเวอร์ชั่นสำหรับเราผู้ใช้ Framework ที่ Microsoft ยกเลิก Array.Clearเร็วกว่าและเร็วกว่าโซลูชันของ Panos Theof ถึง 4 เท่าและEric J'sและPetar Petrov ขนานหนึ่งถึงสองเท่าเร็วสำหรับอาเรย์ขนาดใหญ่

ก่อนอื่นฉันต้องการให้คุณทราบถึงบรรพบุรุษของฟังก์ชั่นเพราะมันทำให้ง่ายต่อการเข้าใจโค้ด ประสิทธิภาพที่ชาญฉลาดนี่เป็นสิ่งที่ดีมากเมื่อเทียบกับรหัสของ Panos Theof และสำหรับบางสิ่งที่อาจเพียงพอแล้ว:

public static void Fill<T> (T[] array, int count, T value, int threshold = 32)
{
    if (threshold <= 0)
        throw new ArgumentException("threshold");

    int current_size = 0, keep_looping_up_to = Math.Min(count, threshold);

    while (current_size < keep_looping_up_to)
        array[current_size++] = value;

    for (int at_least_half = (count + 1) >> 1; current_size < at_least_half; current_size <<= 1)
        Array.Copy(array, 0, array, current_size, current_size);

    Array.Copy(array, 0, array, current_size, count - current_size);
}

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

const int ARRAY_COPY_THRESHOLD = 32;  // 16 ... 64 work equally well for all tested constellations
const int L1_CACHE_SIZE = 1 << 15;

public static void Fill<T> (T[] array, int count, T value, int element_size)
{
    int current_size = 0, keep_looping_up_to = Math.Min(count, ARRAY_COPY_THRESHOLD);

    while (current_size < keep_looping_up_to)
        array[current_size++] = value;

    int block_size = L1_CACHE_SIZE / element_size / 2;
    int keep_doubling_up_to = Math.Min(block_size, count >> 1);

    for ( ; current_size < keep_doubling_up_to; current_size <<= 1)
        Array.Copy(array, 0, array, current_size, current_size);

    for (int enough = count - block_size; current_size < enough; current_size += block_size)
        Array.Copy(array, 0, array, current_size, block_size);

    Array.Copy(array, 0, array, current_size, count - current_size);
}

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

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

  • การประเมินขนาดองค์ประกอบต่ำเกินไปอาจทำให้ขนาดบล็อกเกินกว่าครึ่งหนึ่งของแคช L1 ดังนั้นการเพิ่มโอกาสในการคัดลอกแหล่งข้อมูลที่ได้รับมาจาก L1 และต้องดึงข้อมูลจากระดับแคชที่ช้าลง

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

นี่คือมาตรฐานอ้างอิงรหัสของฉันArray.Clearและอีกสามโซลูชั่นที่กล่าวถึงก่อนหน้านี้ การกำหนดเวลาใช้สำหรับการกรอกอาร์เรย์จำนวนเต็ม ( Int32[]) ของขนาดที่กำหนด เพื่อลดความผันแปรที่เกิดจากความไม่แน่นอนของแคช ฯลฯ การทดสอบแต่ละครั้งจะถูกดำเนินการสองครั้งกลับไปด้านหลังและกำหนดเวลาสำหรับการดำเนินการครั้งที่สอง

array size   Array.Clear      Eric J.   Panos Theof  Petar Petrov   Darth Gizka
-------------------------------------------------------------------------------
     1000:       0,7 µs        0,2 µs        0,2 µs        6,8 µs       0,2 µs 
    10000:       8,0 µs        1,4 µs        1,2 µs        7,8 µs       0,9 µs 
   100000:      72,4 µs       12,4 µs        8,2 µs       33,6 µs       7,5 µs 
  1000000:     652,9 µs      135,8 µs      101,6 µs      197,7 µs      71,6 µs 
 10000000:    7182,6 µs     4174,9 µs     5193,3 µs     3691,5 µs    1658,1 µs 
100000000:   67142,3 µs    44853,3 µs    51372,5 µs    35195,5 µs   16585,1 µs 

หากประสิทธิภาพของรหัสนี้ไม่เพียงพอช่องทางที่สัญญาจะขนานกับลูปการคัดลอกเชิงเส้น (กับเธรดทั้งหมดที่ใช้บล็อกแหล่งที่มาเดียวกัน) หรือ P / Invoke เพื่อนเก่าที่ดีของเรา

หมายเหตุ: การล้างและการเติมบล็อกโดยปกติแล้วจะทำโดยรูทีนย่อยไปยังโค้ดที่มีความเชี่ยวชาญสูงโดยใช้คำสั่ง MMX / SSE และ whatnot ดังนั้นในสภาพแวดล้อมที่เหมาะสมเราจะเรียกคุณธรรมที่เกี่ยวข้องstd::memsetและมั่นใจในประสิทธิภาพระดับมืออาชีพ IOW โดยสิทธิ์ฟังก์ชั่นห้องสมุดArray.Clearควรปล่อยเวอร์ชั่นมือรีดของเราในฝุ่น ความจริงที่ว่ามันเป็นวิธีอื่น ๆ รอบ ๆ แสดงให้เห็นว่าไกลจากสิ่งที่ตีจริงๆ กันไปสำหรับการต้องม้วนตัวเองFill<>ในสถานที่แรกเพราะมันยังคงอยู่ในแกนกลางและมาตรฐาน แต่ไม่ได้อยู่ในกรอบ . NET ได้รับรอบเกือบยี่สิบปีแล้วและเรายังต้อง P / Invoke ซ้ายและขวาสำหรับสิ่งพื้นฐานที่สุดหรือม้วนของเราเอง ...


1

มีคำตอบเพิ่มเติมสำหรับคำถาม (ซ้ำกันหรือไม่): นี้อะไรอะไรคือสิ่งที่เทียบเท่าของ memset ใน C #?

มีคนเปรียบเทียบตัวเลือก (รวมรุ่นที่ไม่ปลอดภัย แต่ไม่ลองmemset): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.html


0

นี่คืออีกหนึ่งการประเมินSystem.Collections.BitArrayที่มีตัวสร้างเช่นนี้

bool[] result = new BitArray(1000000, true).Cast<bool>().ToArray();

หรือ

bool[] result = new bool[1000000];
new BitArray(1000000, true).CopyTo(result, 0);

0

สร้างคลาสส่วนตัวภายในที่คุณสร้างอาเรย์และมีทะเยอทะยานและตัวตั้งค่าสำหรับมัน ถ้าคุณไม่ต้องการให้แต่ละตำแหน่งในอาเรย์เป็นสิ่งที่แปลกใหม่อย่างสุ่มแล้วใช้ int? เป็นอาร์เรย์และจากนั้นรับถ้าตำแหน่งเท่ากับ null เติมตำแหน่งนั้นและส่งกลับค่าสุ่มใหม่

IsVisibleHandler
{

  private bool[] b = new bool[10000];

  public bool GetIsVisible(int x)
  {
  return !b[x]
  }

  public void SetIsVisibleTrueAt(int x)
  {
  b[x] = false //!true
  }
}

หรือใช้

public void SetIsVisibleAt(int x, bool isTrue)
{
b[x] = !isTrue;
}

เหมือนสุนัขเซทเทอร์


-2
Boolean[] data = new Boolean[25];

new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);

โปรดใช้การจัดรูปแบบที่ดีขึ้นและอาจใช้คำอธิบายเล็กน้อยเพื่อให้ผู้อื่นเข้าใจโซลูชันของคุณดีขึ้น
Gorgsenegger

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