Làm cách nào để điền / khởi tạo một mảng C # với một giá trị?


205

Tôi biết rằng các mảng tức thời của các loại giá trị trong C # được tự động điền với giá trị mặc định của loại (ví dụ: false cho bool, 0 cho int, v.v.).

Có cách nào để tự động điền vào một mảng với giá trị seed không phải là mặc định không? Sau đó, khi tạo hoặc một phương thức tích hợp sẵn (như Arrays.fill ()) của Java ? Nói rằng tôi muốn một mảng boolean là đúng theo mặc định, thay vì sai. Có một cách tích hợp để làm điều này, hoặc bạn chỉ phải lặp qua mảng với một vòng lặp for?

 // 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;
 }

Phải lặp lại qua mảng và "thiết lập lại" từng giá trị thành true có vẻ không hiệu quả. Có cách nào để khắc phục điều này? Có lẽ bằng cách lật tất cả các giá trị?

Sau khi gõ câu hỏi này và suy nghĩ về nó, tôi đoán rằng các giá trị mặc định chỉ đơn giản là kết quả của cách C # xử lý việc phân bổ bộ nhớ của các đối tượng này phía sau hậu trường, vì vậy tôi tưởng tượng có lẽ không thể làm điều này. Nhưng tôi vẫn muốn biết chắc chắn!


Tôi thường thay đổi tên từ is_found thành is_still_hiding. Mặc dù yêu thích các câu trả lời, tôi cần phải làm tương tự cho mảng int trong một trường hợp thử nghiệm. (câu hỏi hay)
ctrl-alt-delor 16/11/11

Câu trả lời:


146

Không biết về một phương pháp khung nhưng bạn có thể viết một người trợ giúp nhanh để làm điều đó cho bạn.

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

3
Thích ++ i thay vì i ++ nếu bạn không cần bản sao.
void.pulum

24
i ++ sao chép i, tăng i và trả về giá trị ban đầu. ++ tôi chỉ trả về giá trị tăng dần. Do đó, ++ i nhanh hơn, có thể có ý nghĩa trong các vòng lặp lớn như chúng ta đang nói ở đây.
tenpn

57
@RobertDailey: Đó là một tối ưu hóa trình biên dịch, và không còn đúng nữa. Tôi vừa thử nghiệm để xác minh niềm tin của mình: Nếu giá trị trả về của i ++ không được sử dụng cho bất cứ điều gì, thì trình biên dịch sẽ tự động biên dịch nó thành ++ i tự động cho bạn. Ngoài ra, ngay cả khi tôi sử dụng giá trị trả về, chênh lệch hiệu suất rất nhỏ, tôi cần phải tạo ra một trường hợp cực đoan để đo lường nó. Thậm chí sau đó, nó chỉ dẫn đến một vài phần trăm thời gian chạy khác nhau.
Edward Ned Harvey

8
Tôi đã viết một phương thức mở rộng như thế này nhưng tôi đã có nó trả về mảng ban đầu để cho phép xâu chuỗi phương thức, chẳng hạn như:int[] arr = new int[16].Populate(-1);
Gutblender

2
Thay đổi voidthành T[]và sau đó bạn có thể làmvar a = new int[100].Polupate(1)
orad

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

70
Mặc dù cách này hiệu quả nhưng nó không thực sự là một giải pháp tốt vì nó rất chậm; Trên thực tế, nó chậm hơn khoảng 4 lần so với việc lặp lại với một vòng lặp for.
patjbs

4
đúng vậy, khi chúng tôi xem xét hiệu suất thì vòng lặp for nhanh hơn
Rony

6
Để xem một số điểm chuẩn thực sự, hãy xem C # Khởi tạo mảng .
theknut

4
Enumerable.ToArraykhông biết kích thước của chuỗi vô số, vì vậy nó phải đoán kích thước mảng. Điều đó có nghĩa là bạn sẽ nhận được phân bổ mảng mỗi lần ToArrayvượt quá bộ đệm, cộng thêm một phân bổ nữa ở cuối cho phần cắt. Ngoài ra còn có chi phí liên quan với các đối tượng vô số.
Edward Brey

5
Chỉ cần một lưu ý, với các kiểu tham chiếu, điều này sẽ lấp đầy toàn bộ mảng với tất cả các tham chiếu đến cùng một đối tượng. Nếu đây không phải là điều bạn muốn và bạn thực sự muốn tạo các đối tượng khác nhau cho từng mục mảng, hãy xem stackoverflow.com/a/44937053/23715 .
Alex Che

74

Tạo một mảng mới với một nghìn truegiá trị:

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

Tương tự, bạn có thể tạo các chuỗi số nguyên:

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

8
Không tệ, nhưng nó vẫn chậm hơn một vòng lặp for với hệ số 4x
patjbs

1
patjbs trong lý thuyết trong tương lai Enumerable.Repeat sẽ thực hiện nhanh hơn bởi vì nó sẽ sử dụng một triển khai song song.
Petar Petrov

1
@PetarPetrov Điều này sẽ không bao giờ xảy ra do đập bộ đệm. Tôi khá chắc chắn rằng do bản chất của bộ đệm CPU, việc thực hiện công việc song song trên một mảng sẽ luôn chậm hơn bất kể điều gì vì máy tính mong đợi công việc đồng bộ và tải dữ liệu phù hợp.
TernaryToperator

dự định bi quan! = thiếu tối ưu hóa sớm.
Denis Gladkiy

24

Đối với các mảng lớn hoặc mảng sẽ có kích thước thay đổi, có lẽ bạn nên sử dụng:

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

Đối với mảng nhỏ, bạn có thể sử dụng cú pháp khởi tạo bộ sưu tập trong C # 3:

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

Lợi ích của cú pháp khởi tạo bộ sưu tập là bạn không phải sử dụng cùng một giá trị trong mỗi vị trí và bạn có thể sử dụng biểu thức hoặc hàm để khởi tạo vị trí. Ngoài ra, tôi nghĩ rằng bạn tránh chi phí khởi tạo vị trí mảng thành giá trị mặc định. Ví dụ:

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

Và để khởi tạo một mảng float []:float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
Jim Lahman

Việc khởi tạo FWIW của một mảng có thể được thực hiện trong bất kỳ phiên bản C # nào như:bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Peter van der Heijden 23/2/2015

24

Nếu mảng của bạn quá lớn, bạn nên sử dụng BitArray. Nó sử dụng 1 bit cho mỗi bool thay vì một byte (như trong một mảng các bool), bạn cũng có thể đặt tất cả các bit thành true với các toán tử bit. Hoặc chỉ khởi tạo đúng. Nếu bạn chỉ cần làm một lần, nó sẽ chỉ tốn nhiều tiền hơn.

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

Bạn có thể sử dụng Array.Filltrong .NET Core 2.0+ và .NET Standard 2.1+.


Thông minh! Mặc dù phải biết rằng đó là một phương pháp tương đối mới. Nó có sẵn trong .NET Core 2.0+ và .NET Standard 2.1, nhưng đặc biệt không có trong bất kỳ phiên bản .NET Framework nào. (nó sẽ có trong .NET 5.0, kết hợp .NET Framework và .NET Core với nhau).
Abel

9

Thật không may, tôi không nghĩ có một cách trực tiếp, tuy nhiên tôi nghĩ bạn có thể viết một phương thức mở rộng cho lớp mảng để làm điều này

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;
        }
    }
}

Tôi thích ý tưởng mở rộng càng đào sâu vào vấn đề này. Đôi khi giải pháp trả trước và đơn giản thực sự là tốt nhất!
patjbs

8

Sau khi đọc thêm một chút và đọc, tôi thấy điều này:

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

Mà chắc chắn là gần hơn với những gì tôi đang tìm kiếm. Nhưng tôi không chắc liệu điều đó có tốt hơn việc lặp qua mảng ban đầu trong một vòng lặp for và chỉ thay đổi các giá trị. Sau khi thử nghiệm nhanh trên thực tế, nó xuất hiện chậm hơn khoảng 5 lần. Vì vậy, đây không thực sự là một giải pháp tốt!


4
tương tự như những gì bạn đang cố gắng thực hiện, ngoại trừ việc thực hiện chức năng gọi cho từng phần tử trong mảng của bạn. Nó có thể trông đẹp hơn về mặt cú pháp, nhưng nó làm được nhiều việc hơn ...
Nader Shirazie

vâng, nó trông giống như một vòng lặp đơn giản để thực hiện công việc cũng như mọi thứ khác
patjbs

Nó tạo ra một mảng mới (không thay đổi thể hiện ban đầu).
Jeppe Stig Nielsen

7

Điều gì về việc thực hiện song song

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);
}

Khi chỉ khởi tạo một mảng, không thể nhìn thấy sức mạnh của mã này nhưng tôi nghĩ bạn chắc chắn nên quên đi "thuần túy".


Điều này có nguy cơ xảy ra vấn đề chia sẻ sai, trong đó các luồng khác nhau cạnh tranh cho các dòng bộ đệm CPU và do đó làm giảm hiệu suất so với triển khai một luồng. Việc đó có xảy ra hay không phụ thuộc vào kích thước của các khối bộ nhớ trên mỗi luồng và kiến ​​trúc CPU.
Eric J.

7

Mã dưới đây kết hợp phép lặp đơn giản cho các bản sao nhỏ và Array.Copy cho các bản sao lớn

    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;
            }
        }
    }

Các điểm chuẩn cho độ dài mảng khác nhau bằng cách sử dụng mảng int [] là:

         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

Các cột đầu tiên là kích thước mảng, theo sau là thời gian sao chép bằng cách sử dụng phép lặp đơn giản (triển khai @JaredPared). Thời gian của phương pháp này là sau đó. Đây là các điểm chuẩn sử dụng một mảng gồm một cấu trúc gồm bốn số nguyên

         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

Hoặc ... bạn chỉ có thể sử dụng logic đảo ngược. Hãy để falseý truevà ngược lại.

Mẫu mã

// 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!
}

Giải pháp hài hước, tất cả mặc dù điều này sẽ khó hơn rất nhiều với ví dụ ints vì bạn mất 0.
MrFox

1
đây thực sự là một lựa chọn khả thi nếu bạn "đảo ngược logic" trên tên biến: thay vì bool[] isVisiblethực hiệnbool[] isHidden
Markus Hütter

1
Mọi người dường như phản ứng như thế này là một loại hack vui nhộn. Đây là một kỹ thuật tối ưu hóa phổ biến. Nếu bạn may mắn, trình biên dịch sẽ làm điều này cho bạn.
l33t

4

cái này cũng hoạt động ... nhưng có thể không cần thiết

 bool[] abValues = new bool[1000];
 abValues = abValues.Select( n => n = true ).ToArray<bool>();

4

Nhiều câu trả lời được trình bày ở đây thực hiện theo một vòng lặp khởi tạo một phần tử mảng một lần, không tận dụng các hướng dẫn CPU được thiết kế để hoạt động trên một khối bộ nhớ cùng một lúc.

.Net Standard 2.1 (trong bản xem trước khi viết bài này) cung cấp Array.Fill () , cho vay để thực hiện hiệu năng cao trong thư viện thời gian chạy (mặc dù hiện tại, .NET Core dường như không tận dụng khả năng đó) .

Đối với những người trên các nền tảng trước đó, phương thức tiện ích mở rộng sau vượt trội hơn một vòng lặp tầm thường bởi một lề đáng kể khi kích thước mảng là đáng kể. Tôi đã tạo nó khi giải pháp cho thử thách mã trực tuyến của tôi là khoảng 20% ​​trong ngân sách thời gian được phân bổ. Nó giảm thời gian chạy khoảng 70%. Trong trường hợp này, việc điền vào mảng được thực hiện bên trong một vòng lặp khác. BLOCK_SIZE được đặt theo cảm giác ruột hơn là thử nghiệm. Một số tối ưu hóa là có thể (ví dụ: sao chép tất cả các byte đã được đặt thành giá trị mong muốn thay vì khối có kích thước cố định).

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

Nếu bạn dự định chỉ đặt một vài giá trị trong mảng, nhưng muốn nhận giá trị mặc định (tùy chỉnh) hầu hết thời gian, bạn có thể thử một cái gì đó như thế này:

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; }
    }
}

Có lẽ bạn sẽ cần phải thực hiện các giao diện khác để làm cho nó hữu ích, chẳng hạn như các giao diện trên chính mảng .


3

Không có cách nào để đặt tất cả các phần tử trong một mảng thành một hoạt động đơn lẻ, UNLESS, giá trị đó là giá trị mặc định của các loại phần tử.

Ví dụ: nếu đó là một mảng các số nguyên, bạn có thể đặt tất cả chúng về 0 chỉ với một thao tác, như vậy: Array.Clear(...)


2

Tôi nhận ra rằng tôi đến bữa tiệc muộn nhưng đây là một ý tưởng. Viết một trình bao bọc có các toán tử chuyển đổi đến và từ giá trị được bọc để nó có thể được sử dụng làm giá trị thay thế cho loại được bọc. Điều này thực sự được lấy cảm hứng từ câu trả lời ngớ ngẩn từ @ l33t.

Đầu tiên (đến từ C ++) Tôi nhận ra rằng trong C #, một ctor mặc định không được gọi khi các phần tử của một mảng được xây dựng. Thay vào đó - ngay cả khi có mặt của hàm tạo mặc định do người dùng định nghĩa! - tất cả các phần tử mảng được khởi tạo bằng không. Điều đó làm tôi ngạc nhiên.

Vì vậy, một lớp bao bọc chỉ đơn giản cung cấp một ctor mặc định với giá trị mong muốn sẽ hoạt động cho các mảng trong C ++ nhưng không phải trong C #. Cách giải quyết là để cho phép trình bao bọc ánh xạ 0 đến giá trị hạt giống mong muốn khi chuyển đổi. Bằng cách đó, các giá trị khởi tạo bằng 0 dường như được khởi tạo với hạt giống cho tất cả các mục đích thực tế:

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]);
}

Mẫu này được áp dụng cho tất cả các loại giá trị. Ví dụ, người ta có thể ánh xạ 0 đến 4 cho ints nếu muốn khởi tạo với 4, v.v.

Tôi muốn tạo một mẫu của nó như có thể có trong C ++, cung cấp giá trị hạt giống như tham số mẫu, nhưng tôi hiểu điều đó là không thể trong C #. Hay tôi đang thiếu một cái gì đó? (Tất nhiên trong ánh xạ C ++ hoàn toàn không cần thiết bởi vì người ta có thể cung cấp một ctor mặc định sẽ được gọi cho các phần tử mảng.)

FWIW, đây là một C ++ tương đương: https://ideone.com/wG8yEh .


2

Nếu bạn có thể đảo ngược logic của mình, bạn có thể sử dụng Array.Clear()phương thức để đặt mảng boolean thành false.

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

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

2

Nếu bạn đang sử dụng .NET Core, .NET Standard> = 2.1 hoặc phụ thuộc vào gói System.Memory, bạn cũng có thể sử dụng Span<T>.Fill()phương thức:

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

Đây là một phiên bản khác cho chúng tôi Người dùng khung bị Microsoft bỏ rơi. Nó nhanh gấp 4 lần Array.Clearvà nhanh hơn giải pháp của Panos Theofsong song của Eric JPetar Petrov - nhanh gấp hai lần đối với các mảng lớn.

Đầu tiên tôi muốn trình bày cho bạn tổ tiên của hàm, bởi vì điều đó giúp dễ hiểu mã hơn. Hiệu suất-khôn ngoan này tương đương với mã của Panos Theof và đối với một số thứ có thể là đủ:

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);
}

Như bạn có thể thấy, điều này dựa trên việc nhân đôi nhiều lần phần đã được khởi tạo. Điều này là đơn giản và hiệu quả, nhưng nó chạy trên kiến ​​trúc bộ nhớ hiện đại. Do đó, sinh ra một phiên bản chỉ sử dụng nhân đôi để tạo khối hạt giống thân thiện với bộ đệm, sau đó được thổi lặp đi lặp lại trên khu vực mục tiêu:

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);
}

Lưu ý: mã trước đó cần (count + 1) >> 1làm giới hạn cho vòng lặp nhân đôi để đảm bảo rằng thao tác sao chép cuối cùng có đủ thức ăn để bao gồm tất cả những gì còn lại. Điều này sẽ không xảy ra đối với số lượng lẻ nếu count >> 1được sử dụng thay thế. Đối với phiên bản hiện tại, điều này không có ý nghĩa gì vì vòng lặp sao chép tuyến tính sẽ thu được bất kỳ sự chậm chạp nào.

Kích thước của một ô mảng phải được truyền dưới dạng tham số vì - kính bảo vệ tâm trí - thuốc generic không được phép sử dụng sizeoftrừ khi chúng sử dụng một ràng buộc ( unmanaged) có thể có hoặc không có sẵn trong tương lai. Ước tính sai không phải là một vấn đề lớn nhưng hiệu suất là tốt nhất nếu giá trị là chính xác, vì những lý do sau:

  • Việc đánh giá thấp kích thước phần tử có thể dẫn đến kích thước khối lớn hơn một nửa bộ đệm L1, do đó làm tăng khả năng dữ liệu nguồn sao chép bị xóa khỏi L1 và phải được tìm nạp lại từ các mức bộ đệm chậm hơn.

  • Đánh giá quá cao kích thước phần tử dẫn đến việc sử dụng không đúng bộ đệm L1 của CPU, có nghĩa là vòng lặp sao chép khối tuyến tính được thực thi thường xuyên hơn so với việc sử dụng tối ưu. Do đó, nhiều chi phí vòng lặp / cuộc gọi cố định phát sinh hơn mức cần thiết.

Đây là một điểm chuẩn đưa mã của tôi chống lại Array.Clearvà ba giải pháp khác đã đề cập trước đó. Thời gian là để điền vào các mảng số nguyên ( Int32[]) của các kích thước đã cho. Để giảm sự thay đổi gây ra bởi các bộ nhớ cache, v.v., mỗi bài kiểm tra được thực hiện hai lần, quay lại và thời gian được thực hiện cho lần thực hiện thứ hai.

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 

Nếu hiệu suất của mã này không đủ thì một con đường đầy hứa hẹn sẽ song song với vòng lặp sao chép tuyến tính (với tất cả các luồng sử dụng cùng một khối nguồn) hoặc P / Invoke, người bạn cũ tốt của chúng tôi.

Lưu ý: việc xóa và điền khối thường được thực hiện bằng các thói quen thời gian chạy phân nhánh thành mã chuyên dụng cao bằng cách sử dụng các hướng dẫn MMX / SSE và vì vậy, trong bất kỳ môi trường tốt nào, người ta chỉ cần gọi tương đương đạo đức tương ứng std::memsetvà được đảm bảo về mức hiệu suất chuyên nghiệp. IOW, theo quyền, chức năng thư viện Array.Clearsẽ để lại tất cả các phiên bản cuộn tay của chúng tôi trong bụi. Thực tế là cách khác xung quanh cho thấy những thứ thực sự xa vời đến mức nào. Điều tương tự cũng xảy ra khi bạn phải tự quay vòng Fill<>ở vị trí đầu tiên, bởi vì nó vẫn chỉ ở trong Core và Standard chứ không phải trong Framework. .NET đã tồn tại được gần hai mươi năm nay và chúng tôi vẫn phải P / Gọi trái và phải cho những thứ cơ bản nhất hoặc tự cuộn ...



0

Đây là một thẩm định khác System.Collections.BitArraycó xây dựng như vậy.

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

hoặc là

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

0

Tạo một lớp riêng bên trong nơi bạn tạo mảng và có một getter và setter cho nó. Trừ khi bạn cần mỗi vị trí trong mảng là một cái gì đó duy nhất, như ngẫu nhiên, sau đó sử dụng int? dưới dạng một mảng và sau đó nhận được nếu vị trí bằng null điền vào vị trí đó và trả về giá trị ngẫu nhiên mới.

IsVisibleHandler
{

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

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

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

Hoặc dùng

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

Như setter.


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

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

Vui lòng sử dụng định dạng tốt hơn và có thể một vài từ giải thích để người khác có thể hiểu giải pháp của bạn tốt hơn.
Gorgsenegger

1
Bạn có thể sử dụng điều này để tăng hiệu suất khởi tạo bằng cách phân vùng mảng đích và sao chép hạt giống vào các phân vùng khác nhau. Điều này chỉ có ý nghĩa để đưa ra một ý tưởng - Đây là bài viết đầu tiên của tôi và là bài đăng cuối cùng của tôi.
ldsmithperrin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.