كيف ومتى التخلي عن استخدام المصفوفات في C# ؟

StackOverflow https://stackoverflow.com/questions/75976

  •  09-06-2019
  •  | 
  •  

سؤال

لطالما قيل أن إضافة عنصر إلى مجموعة يحدث مثل هذا:

فارغة نسخ من مجموعة+1element هو إنشاء ثم البيانات من مجموعة الأصلي يتم نسخها إلى ذلك ثم البيانات الجديدة على عنصر جديد هو ثم تحميل

إذا كان هذا صحيحا, ثم استخدام مجموعة ضمن السيناريو الذي يتطلب الكثير من عنصر النشاط هو مضاد استطباب بسبب الذاكرة واستخدام وحدة المعالجة المركزية, صحيح ؟

إذا كان هذا هو الحال, ألا يجب عليك محاولة تجنب استخدام صفيف قدر الإمكان عندما سيتم إضافة الكثير من العناصر ؟ يجب عليك استخدام iStringMap بدلا من ذلك ؟ إذا ماذا يحدث إذا كنت تحتاج إلى أكثر من بعدين و تحتاج إلى إضافة الكثير من العناصر الإضافات.هل تأخذ فقط الأداء ضرب أو هل هناك شيء آخر يجب استخدامها ؟

هل كانت مفيدة؟

المحلول

نظرة عامة List<T> كبديل عن المصفوفات.فهي تدعم معظم الأشياء نفسها المصفوفات القيام به ، بما في ذلك تخصيص أولي حجم التخزين إذا كنت تريد.

نصائح أخرى

هذا حقا يتوقف على ما تعنيه "إضافة".

إذا كنت تقصد:

T[] array;
int i;
T value;
...
if (i >= 0 && i <= array.Length)
    array[i] = value;

ثم, لا, هذا لا إنشاء مجموعة جديدة ، هي في الواقع أسرع طريقة لتغيير أي نوع من IList في .صافي.

ولكن ، إذا كنت تستخدم شيئا مثل ArrayList, قائمة, مجموعة, الخ.ثم يدعو "إضافة" طريقة قد إنشاء مجموعة جديدة -- لكنها ذكية حول هذا الموضوع, لا مجرد تغيير حجم 1 عنصر تنمو هندسيا حتى إذا كنت تقوم بإضافة الكثير من القيم فقط في كل مرة واحدة في حين أنها مضطرة إلى تخصيص مجموعة جديدة.حتى ذلك الحين, يمكنك استخدام "القدرة" الملكية إلى قوة أن تنمو قبل اليد ، إذا كنت تعرف كيفية العديد من العناصر إضافة (list.Capacity += numberOfAddedElements)

بشكل عام أنا أفضل أن تجنب مجموعة الاستخدام.مجرد استخدام القائمة<T>.فإنه يستخدم بشكل حيوي الحجم مجموعة داخليا, و هو سريع بما فيه الكفاية بالنسبة لمعظم الاستخدام.إذا كنت تستخدم multi-dimentional المصفوفات ، استخدم قائمة<List<List<T>>> إذا كان لديك إلى.ليس هذا أسوأ بكثير من حيث الذاكرة و هو أبسط من ذلك بكثير لإضافة بنود.

إذا كنت في 0.1% من الاستخدام الذي يتطلب السرعة القصوى, تأكد من انها قائمة المنافذ التي هي حقا المشكلة قبل محاولة لتحسين ذلك.

إذا كنت تنوي أن تكون إضافة/إزالة العناصر الكثير, مجرد استخدام القائمة.إذا كان الأبعاد, يمكنك دائما استخدام قائمة<List<int>> أو شيء من هذا.

من ناحية أخرى, قوائم هي أقل كفاءة من المصفوفات إذا ما كنت في الغالب به هو تعبر قائمة لأن المصفوفات كلها في مكان واحد في وحدة المعالجة المركزية ذاكرة التخزين المؤقت ، حيث الكائنات في القائمة هي منتشرة في جميع أنحاء المكان.

إذا كنت ترغب في استخدام مجموعة فعالة القراءة ولكن ستكون "مضيفا" عناصر في كثير من الأحيان, لديك خياران رئيسيان:

1) إنشاء قائمة (أو قائمة من قوائم) ومن ثم استخدام ToArray() أن تتحول إلى كفاءة مجموعة هيكل.

2) تخصيص صفيف أن يكون أكبر مما تحتاج ، ثم وضع الأشياء في المخصص قبل الخلايا.إذا كنت تحتاج إلى المزيد من العناصر من قبل المخصصة يمكنك تخصيص مجموعة عندما يملأ مضاعفة حجم كل مرة.وهذا يعطي O(log n) تغيير حجم الأداء بدلا من O(n) مثل ذلك سيكون مع تخصيص-مرة-في-إضافة مجموعة.لاحظ أن هذا هو الى حد كبير كيف بـ stringbuilder يعمل ، مما يتيح لك أسرع طريقة باستمرار إلحاقي إلى سلسلة.

عند التخلي عن استخدام المصفوفات

  1. أولا وقبل كل شيء ، عندما دلالات المصفوفات لا مباراة مع القصد الخاص - الحاجة حيوي متزايد جمع ؟ مجموعة التي لا تسمح التكرارات ؟ المجموعة التي يجب أن تبقى ثابتة?تجنب المصفوفات في كل الحالات.أن 99% من الحالات.فقط بديهيا النقطة الأساسية.

  2. ثانيا ، عندما كنت لا الترميز المطلق الأداء criticalness - ان حوالي 95% من الحالات. المصفوفات أداء أفضل بشكل هامشي, وخاصة في التكرار.انها دائما تقريبا لا يهم.

  3. عندما كنت لا القسري من قبل حجة مع params الكلمات الرئيسية - تمنيت فقط params قبول أي IEnumerable<T> أو حتى أفضل لغة بناء نفسها للدلالة على تسلسل (وليس نوع الإطار).

  4. عندما كنت لا كتابة التعليمات البرمجية القديمة ، أو التعامل مع إمكانية التشغيل المتداخل

وباختصار ، فإن من النادر أن تحتاج في الواقع صفيف.سوف أقوم بإضافة لماذا يمكن تجنب ذلك ؟

  1. أكبر سبب لتجنب المصفوفات المنظمة البحرية الدولية هو المفاهيمي.المصفوفات هي أقرب إلى تنفيذ وأبعد من التجريد.المصفوفات ينقل أكثر كيف يتم ذلك من ما هو القيام به الذي هو ضد الروح من لغات المستوى العالي.هذا ليس من المستغرب النظر في المصفوفات هي أقرب إلى المعدن ، هي على التوالي من نوع خاص (على الرغم داخليا مجموعة فئة).لا يكون التربوية ، ولكن المصفوفات حقا لا تترجم إلى المعنى الدلالي جدا جدا نادرا ما يلزم.الأكثر فائدة كثرة الدلالات هي أن مجموعات مع أي إدخالات مجموعات متميزة مع البنود, قيمة مفتاح الخرائط وغيرها مع أي مزيج من addable ، للقراءة فقط غير قابلة للتغيير ، النظام-احترام المتغيرات.نفكر في هذا ، قد ترغب addable جمع أو للقراءة فقط مع مجموعة محددة مسبقا من البنود مع عدم وجود مزيد من التعديل ، ولكن كيف في كثير من الأحيان لا المنطق الخاص بك تبدو مثل "أريد حيوي addable جمع ولكن فقط عدد ثابت منها وأنها ينبغي أن تكون قابلة للتعديل أيضا" ؟ نادرة جدا وأود أن أقول.

  2. مجموعة صممت خلال مرحلة ما قبل الأدوية عصر وأنه يحاكي genericity مع الكثير من وقت تشغيل الخارقة و سوف تظهر الشذوذ هنا وهناك.بعض من أدرك وجدت:

    1. كسر التغاير.

      string[] strings = ...
      object[] objects = strings;
      objects[0] = 1; //compiles, but gives a runtime exception.
      
    2. المصفوفات يمكن أن تعطيك إشارة إلى البنية!.وهذا على عكس أي مكان آخر.عينة:

      struct Value { public int mutable; }
      
      var array = new[] { new Value() };  
      array[0].mutable = 1; //<-- compiles !
      //a List<Value>[0].mutable = 1; doesnt compile since editing a copy makes no sense
      print array[0].mutable // 1, expected or unexpected? confusing surely
      
    3. وقت التشغيل بتنفيذ طرق مثل ICollection<T>.Contains يمكن أن تكون مختلفة عن البنيات و دروس.انها ليست صفقة كبيرة ، ولكن إذا كنت تنسى أن تجاوز غير عامة Equals صحيح كمرجع أنواع تتوقع جمع عام للبحث عن عامة Equals, سوف تحصل على نتائج غير صحيحة.

      public class Class : IEquatable<Class>
      {
          public bool Equals(Class other)
          {
              Console.WriteLine("generic");
              return true;
          }
          public override bool Equals(object obj)
          {
              Console.WriteLine("non generic");
              return true;
          } 
      }
      
      public struct Struct : IEquatable<Struct>
      {
          public bool Equals(Struct other)
          {
              Console.WriteLine("generic");
              return true;
          }
          public override bool Equals(object obj)
          {
              Console.WriteLine("non generic");
              return true;
          } 
      }
      
      class[].Contains(test); //prints "non generic"
      struct[].Contains(test); //prints "generic"
      
    4. على Length مكان الإقامة ، [] مفهرس على T[] يبدو العادية الخصائص التي يمكنك الوصول إليها من خلال التأمل (التي ينبغي أن تنطوي على بعض السحر) ، ولكن عندما يتعلق الأمر أشجار التعبير يجب أن بصق بنفس الكود البرمجي لا.هناك ArrayLength و ArrayIndex طرق للقيام بذلك بشكل منفصل.واحد مثل هذا السؤال هنا.مثال آخر:

      Expression<Func<string>> e = () => new[] { "a" }[0];
      //e.Body.NodeType == ExpressionType.ArrayIndex
      
      Expression<Func<string>> e = () => new List<string>() { "a" }[0];
      //e.Body.NodeType == ExpressionType.Call;
      

كيفية التخلي عن استخدام المصفوفات

الأكثر شيوعا هو البديل List<T> الذي نظافة API.وإنما هو حيوي متزايد هيكل مما يعني أنك يمكن أن تضيف إلى List<T> في نهاية أو إدراج أي مكان إلى أي قدرة.لا يوجد بديل عن نفس السلوك من مجموعة ، ولكن الناس في الغالب استخدام صفائف للقراءة فقط جمع حيث لا يمكن إضافة أي شيء إلى نهايته.البديل هو ReadOnlyCollection<T>.لقد تحمل هذا التمديد الطريقة:

public ReadOnlyCollection<T> ToReadOnlyCollection<T>(IEnumerable<T> source)
{
    return source.ToList().AsReadOnly();
}

عندما مصفوفة حجمها, مجموعة جديدة يجب تخصيص محتويات نسخها.إذا كنت فقط تعديل محتويات المصفوفة هو مجرد ذكرى المهمة.

لذا يجب عدم استخدام المصفوفات عندما كنت لا تعرف حجم المصفوفة أو حجم المرجح أن تتغير.ومع ذلك, إذا كان لديك طول ثابت صفيف ، فهي طريقة سهلة لاسترجاع قبل عناصر الفهرس.

ArrayList و قائمة تنمو مجموعة من واحد أو أكثر عند الحاجة (أعتقد أنه من خلال مضاعفة حجم ، ولكن لم أتحقق المصدر).وهم عموما أفضل خيار عندما كنت بناء حيوي حجم الصفيف.

عندما المعايير الخاصة بك تشير إلى أن مجموعة حجم بجدية تباطؤ الطلب (تذكر - من السابق لأوانه الأمثل هو أصل كل الشرور) ، يمكنك تقييم الكتابة مخصص فئة مجموعة مع أنب تغيير السلوك.

عموما, إذا كان يجب أن يكون أفضل فهرسة البحث الأداء الأفضل لبناء قائمة أولا ثم تحويله إلى مجموعة مما دفع العقوبة صغيرة في البداية ولكن تجنب أي وقت لاحق.إذا كانت المشكلة هي أنك سوف تكون باستمرار إضافة بيانات جديدة وإزالة البيانات القديمة فإنك قد ترغب في استخدام ArrayList أو قائمة للراحة ولكن نضع في اعتبارنا أن أنها مجرد حالة خاصة المصفوفات.عندما "تنمو" التي تخصص جديد تماما مجموعة نسخ كل ما في الأمر وهو بطيئة للغاية.

ArrayList هو مجرد مجموعة التي تنمو عند الحاجة.إضافة المطفأة O(1), فقط كن حذرا للتأكد من تغيير لن يحدث في وقت سيء.إدراج O(n) كل البنود إلى اليمين يجب أن يتم نقل أكثر.إزالة O(n) كل البنود إلى اليمين يجب أن يتم نقل أكثر.

من المهم أيضا أن نضع في اعتبارنا أن القائمة ليست قائمة مرتبطة.انها مجرد كتابة ArrayList.قائمة الوثائق هل لاحظ أنه يؤدي بشكل أفضل في معظم الحالات ولكن لا يقول لماذا.

أفضل شيء القيام به هو اختيار بنية البيانات التي مناسبا لمشكلتك.هذا يعتمد المرء الكثير من الأشياء و لذلك قد ترغب في تصفح النظام.مجموعات.عامة مساحة الاسم.

في هذه الحالة بالذات أود أن أقول أنه إذا كنت يمكن أن تأتي مع قيمة المفتاح القاموس سيكون أفضل رهان.وقد إدراج وإزالة هذا النهج O(1).ومع ذلك ، حتى مع القاموس عليك أن تكون حذرا لا تدع حجمه الداخلية مجموعة (O(n) العملية).فمن الأفضل أن تعطي لهم الكثير من الغرفة عن طريق تحديد أكبر-ثم هل-تتوقع-استخدام القدرات الأولية في منشئ.

ريك

معيار مجموعة يجب أن تكون محددة مع طول ، والتي تحتفظ كل من الذاكرة التي يحتاجها في كتلة قريبة.إضافة عنصر إلى مجموعة وضعه داخل كتلة بالفعل الذاكرة المحجوزة.

صفائف كبيرة قليل من يكتب و العديد من يقرأ, ولا سيما تلك ذات الطبيعة التكرارية - عن أي شيء آخر ، استخدام واحدة من العديد من غيرها من هياكل البيانات.

كنت على صواب مجموعة كبيرة من أجل عمليات البحث.ومع ذلك تعديلات على حجم المصفوفة مكلفة.

يجب عليك استخدام الحاويات التي تدعم تدريجي حجم التعديلات في السيناريو حيث كنت تعديل حجم المصفوفة.هل يمكن استخدام ArrayList الذي يسمح لك تعيين الحجم الأولي ، ويمكن أن تحقق باستمرار حجم مقابل القدرات ومن ثم زيادة القدرة قطعة كبيرة للحد من عدد من تغيير حجم.

أو هل يمكن أن مجرد استخدام قائمة مرتبطة.ثم ومع ذلك عمليات البحث بطيئة...

هذا منتدى آخر قد أو قد لا تكون ذات فائدة لك بخصوص فعالية مختلف أنواع مجموعة:C# المصفوفات - الأبعاد vs lexicographic

إذا أعتقد أنني ذاهب إلى أن إضافة عناصر إلى مجموعة الكثير على مدى أكثر من سوف تستخدم قائمة.إذا كنت أعرف على وجه اليقين ما حجم المجموعة سوف تكون عندما أعلن ، ثم سوء استخدام صفيف.

مرة أخرى أنا عموما استخدام صفيف قائمة هو عندما كنت في حاجة للعودة إلى جمع عقار كائن لا أريد المتصلين إضافة عناصر تلك المجموعة عبر قائمة إضافة طرق, ولكن بدلا من ذلك تريد منهم أن إضافة عناصر إلى مجموعة عن طريق بلدي كائن واجهة.في هذه الحالة سوف يستغرق الداخلية قائمة الاتصال ToArray والعودة صفيف.

إذا كنت تنوي أن تفعل الكثير من إضافة ، و سوف لا تفعل وصول عشوائي (مثل myArray[i]).يمكنك أن تنظر في استخدام قائمة مرتبطة (LinkedList<T>) ، لأنها لن تضطر إلى "النمو" مثل List<T> التنفيذ.نضع في اعتبارنا ، على الرغم من أنه يمكنك فقط حقا الوصول إلى العناصر في LinkedList<T> تنفيذ باستخدام IEnumerable<T> واجهة.

أفضل شيء يمكنك القيام به هو تخصيص الذاكرة قدر عليك مقدما إذا كان ذلك ممكنا.هذا سوف يمنع .صافي من الحاجة إلى بذل المزيد من المكالمات للحصول على الذاكرة في كومة الذاكرة المؤقتة.إذا تعذر ذلك ثم فمن المنطقي أن تخصص في قطع من خمسة أو أيا كان عدد المنطقي للتطبيق الخاص بك.

هذا قاعدة عامة يمكن تطبيقها على أي شيء حقا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top