1. الشرح
فيما يتعلق بطريقة اجتياز الخريطة في Java، توصي العديد من المقالات باستخدامentrySet، والتي تعتبر أكثر كفاءة من keySet. السبب هو: تحصل طريقة الإدخال على مجموعة المفاتيح والقيم مرة واحدة، بينما تحصل مجموعة المفاتيح على مجموعة المفاتيح فقط لكل مفتاح، ويجب البحث عن القيمة في الخريطة مرة أخرى، وبالتالي تقليل عدد المفاتيح الكفاءة الشاملة. إذن ما هو الوضع الفعلي؟
من أجل فهم الفجوة الحقيقية في أداء الاجتياز، بما في ذلك الاختلافات في السيناريوهات المختلفة مثل اجتياز المفتاح + القيمة، واجتياز المفتاح، واجتياز القيمة، وما إلى ذلك، حاولت إجراء بعض الاختبارات المقارنة.
2. الاختبار المقارن
في البداية، تم إجراء اختبار بسيط فقط، لكن النتائج أظهرت أن أداء keySet كان أفضل، الأمر الذي حيرني، ألا يقولون جميعًا أنentrySet أفضل من keySet بشكل واضح؟ ومن أجل مزيد من التحقق، تم استخدام بيانات اختبار مختلفة لإجراء اختبارات مقارنة أكثر تفصيلا.
2.1 بيانات الاختبار
2.1.1 بيانات اختبار HashMap
HashMap-1، الحجم هو 1 مليون، المفتاح والقيمة كلاهما سلسلة، قيمة المفتاح هي 1، 2، 3...1000000:
انسخ رمز الكود كما يلي:
Map<String, String> Map = new HashMap<String, String>();
مفتاح السلسلة، القيمة؛
لـ (i = 1; i <= num; i++) {
مفتاح = "" + أنا؛
القيمة = "القيمة"؛
Map.put(key, value);
}
HashMap-2، الحجم هو 1 مليون، المفتاح والقيمة كلاهما سلسلة، قيمة المفتاح هي 50، 100، 150، 200،...، 50000000:
انسخ رمز الكود كما يلي:
Map<String, String> Map = new HashMap<String, String>();
مفتاح السلسلة، القيمة؛
لـ (i = 1; i <= num; i++) {
مفتاح = "" + (i * 50);
القيمة = "القيمة"؛
Map.put(key, value);
}
2.1.2 بيانات اختبار TreeMap
TreeMap-1، الحجم هو 1 مليون، المفتاح والقيمة كلاهما سلسلة، قيمة المفتاح هي 1، 2، 3...1000000:
انسخ رمز الكود كما يلي:
Map<String, String> Map = new TreeMap<String, String>();
مفتاح السلسلة، القيمة؛
لـ (i = 1; i <= num; i++) {
مفتاح = "" + أنا؛
القيمة = "القيمة"؛
Map.put(key, value);
}
TreeMap-2، الحجم هو مليون، المفتاح والقيمة كلاهما سلسلة، القيم الرئيسية هي 50، 100، 150، 200،...، 50000000، أكثر منفصلة:
انسخ رمز الكود كما يلي:
Map<String, String> Map = new TreeMap<String, String>();
مفتاح السلسلة، القيمة؛
لـ (i = 1; i <= num; i++) {
مفتاح = "" + (i * 50);
القيمة = "القيمة"؛
Map.put(key, value);
}
2.2 سيناريو الاختبار
استخدم طرق كتابة مختلفة لـ keySet وentrySet والقيم لاختبار ثلاثة سيناريوهات: اجتياز المفتاح + القيمة، واجتياز المفتاح، واجتياز القيمة.
2.2.1 مفتاح الاجتياز + القيمة
keySet يجتاز المفتاح + القيمة (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<String> iter = Map.keySet().iterator();
بينما (iter.hasNext()) {
مفتاح = iter.next();
القيمة = Map.get(key);
}
keySet يجتاز المفتاح + القيمة (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (مفتاح السلسلة: Map.keySet()) {
القيمة = Map.get(key);
}
تقوم مجموعة الإدخال باجتياز المفتاح + القيمة (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<Entry<String, String>> iter = Map.entrySet().iterator();
إدخال <سلسلة، سلسلة>؛
بينما (iter.hasNext()) {
الإدخال = iter.next();
مفتاح = دخول.getKey();
القيمة = الإدخال.getValue();
}
تقوم مجموعة الإدخال باجتياز المفتاح + القيمة (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (إدخال <سلسلة، سلسلة>: Map.entrySet ()) {
مفتاح = دخول.getKey();
القيمة = الإدخال.getValue();
}
2.2.2 مفتاح العبور
keySet يجتاز المفتاح (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<String> iter = Map.keySet().iterator();
بينما (iter.hasNext()) {
مفتاح = iter.next();
}
keySet يجتاز المفتاح (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (مفتاح السلسلة: Map.keySet()) {
}
مفتاح الإدخال يجتاز المفتاح (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<Entry<String, String>> iter = Map.entrySet().iterator();
بينما (iter.hasNext()) {
مفتاح = iter.next().getKey();
}
مفتاح الإدخال يجتاز المفتاح (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (إدخال <سلسلة، سلسلة>: Map.entrySet()) {
مفتاح = دخول.getKey();
}
2.2.3 قيمة الاجتياز
يجتاز keySet القيمة (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<String> iter = Map.keySet().iterator();
بينما (iter.hasNext()) {
value = Map.get(iter.next());
}
يجتاز keySet القيمة (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (مفتاح السلسلة: Map.keySet()) {
القيمة = Map.get(key);
}
قيمة اجتياز قيمة الإدخال (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<Entry<String, String>> iter = Map.entrySet().iterator();
بينما (iter.hasNext()) {
value = iter.next().getValue();
}
قيمة الإدخال تعبر القيمة (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (إدخال <سلسلة، سلسلة>: Map.entrySet()) {
القيمة = الإدخال.getValue();
}
القيم تعبر القيمة (طريقة الكتابة 1):
انسخ رمز الكود كما يلي:
Iterator<String> iter = Map.values().iterator();
بينما (iter.hasNext()) {
القيمة = iter.next();
}
القيم تعبر القيمة (طريقة الكتابة 2):
انسخ رمز الكود كما يلي:
لـ (قيمة السلسلة: Map.values()) {
}
2.3 نتائج الاختبار
2.3.1 نتائج اختبار HashMap
الوحدة: ميلي ثانية | خريطة التجزئة-1 | خريطة التجزئة-2 |
| keySet تعبر المفتاح + القيمة (طريقة الكتابة 1) | 39 | 93 |
| keySet يجتاز المفتاح + القيمة (طريقة الكتابة 2) | 38 | 87 |
| تقوم مجموعة الإدخال باجتياز المفتاح + القيمة (طريقة الكتابة 1) | 43 | 86 |
| تقوم مجموعة الإدخال باجتياز المفتاح + القيمة (طريقة الكتابة 2) | 43 | 85 |
الوحدة: ميلي ثانية | خريطة التجزئة-1 | خريطة التجزئة-2 |
| keySet يجتاز المفتاح (طريقة الكتابة 1) | 27 | 65 |
| keySet يجتاز المفتاح (طريقة الكتابة 2) | 26 | 64 |
| مفتاح الإدخال يجتاز المفتاح (طريقة الكتابة 1) | 35 | 75 |
| مفتاح الإدخال يجتاز المفتاح (طريقة الكتابة 2) | 34 | 74 |
الوحدة: ميلي ثانية | خريطة التجزئة-1 | خريطة التجزئة-2 |
| مجموعة المفاتيح تعبر القيمة (طريقة الكتابة 1) | 38 | 87 |
| مجموعة المفاتيح تعبر القيمة (طريقة الكتابة 2) | 37 | 87 |
| قيمة الإدخال تعبر القيمة (طريقة الكتابة 1) | 34 | 61 |
| قيمة الإدخال تعبر القيمة (طريقة الكتابة 2) | 32 | 62 |
| القيم اجتياز القيمة (طريقة الكتابة 1) | 26 | 48 |
| القيم اجتياز القيمة (طريقة الكتابة 2) | 26 | 48 |
2.3.2 نتائج اختبار TreeMap
الوحدة: ميلي ثانية | خريطة الشجرة-1 | خريطة الشجرة-2 |
| keySet تعبر المفتاح + القيمة (طريقة الكتابة 1) | 430 | 451 |
| keySet يجتاز المفتاح + القيمة (طريقة الكتابة 2) | 429 | 450 |
| تقوم مجموعة الإدخال باجتياز المفتاح + القيمة (طريقة الكتابة 1) | 77 | 84 |
| تقوم مجموعة الإدخال باجتياز المفتاح + القيمة (طريقة الكتابة 2) | 70 | 68 |
الوحدة: ميلي ثانية | خريطة الشجرة-1 | خريطة الشجرة-2 |
| keySet يجتاز المفتاح (طريقة الكتابة 1) | 50 | 49 |
| keySet يجتاز المفتاح (طريقة الكتابة 2) | 49 | 48 |
| مفتاح الإدخال يجتاز المفتاح (طريقة الكتابة 1) | 66 | 64 |
| مفتاح الإدخال يجتاز المفتاح (طريقة الكتابة 2) | 65 | 63 |
الوحدة: ميلي ثانية | خريطة الشجرة-1 | خريطة الشجرة-2 |
| مجموعة المفاتيح تعبر القيمة (طريقة الكتابة 1) | 432 | 448 |
| مجموعة المفاتيح تعبر القيمة (طريقة الكتابة 2) | 430 | 448 |
| قيمة الإدخال تعبر القيمة (طريقة الكتابة 1) | 62 | 61 |
| قيمة الإدخال تعبر القيمة (طريقة الكتابة 2) | 62 | 61 |
| القيم اجتياز القيمة (طريقة الكتابة 1) | 46 | 46 |
| القيم اجتياز القيمة (طريقة الكتابة 2) | 45 | 46 |
3. الاستنتاج
3.1 إذا كنت تستخدم HashMap
1. عند اجتياز المفتاح والقيمة في نفس الوقت، يعتمد اختلاف الأداء بين طريقتي keySet وentrySet على الظروف المحددة للمفتاح، مثل التعقيد (الكائنات المعقدة)، والانفصال، ومعدل التعارض، وما إلى ذلك. بمعنى آخر، يعتمد ذلك على تكلفة البحث عن القيمة في HashMap. إن عملية الإدخال لاسترداد جميع المفاتيح والقيم في وقت واحد لها عبء أداء عندما تكون هذه الخسارة أقل من الحمل الزائد لـ HashMap الذي يبحث عن القيمة، وسوف تنعكس ميزة أداء الإدخال. على سبيل المثال، في اختبار المقارنة أعلاه، عندما يكون المفتاح هو أبسط سلسلة رقمية، قد تكون مجموعة المفاتيح أكثر كفاءة، وتستغرق وقتًا أقل بنسبة 10٪ من مجموعة الإدخال. بشكل عام، يوصى باستخدام inputSet. لأنه عندما يكون المفتاح بسيطًا جدًا، قد يكون أدائه أقل قليلاً من keySet، ولكن يمكن التحكم فيه عندما يصبح المفتاح أكثر تعقيدًا، وسوف تنعكس مزايا enterSet بوضوح. وبطبيعة الحال، يمكننا أن نختار وفقا للحالة الفعلية
2. عند اجتياز المفاتيح فقط، تكون طريقة keySet أكثر ملاءمة، لأنentrySet تحذف أيضًا قيمًا غير مفيدة، مما يهدر الأداء والمساحة. في نتائج الاختبار المذكورة أعلاه، يستغرق keySet وقتًا أقل بنسبة 23% من طريقة الإدخال.
3. عند اجتياز القيمة فقط، يكون استخدام طريقة vlaues هو الخيار الأفضل، كما أن طريقة inputSet أفضل قليلاً من طريقة keySet.
4. من بين طرق الكتابة المختلفة، يوصى باستخدام طريقة الكتابة التالية، وهي أكثر كفاءة قليلاً:
انسخ رمز الكود كما يلي:
لـ (مفتاح السلسلة: Map.keySet()) {
القيمة = Map.get(key);
}
لـ (إدخال <سلسلة، سلسلة>: Map.entrySet ()) {
مفتاح = دخول.getKey();
القيمة = الإدخال.getValue();
}
لـ (قيمة السلسلة: Map.values()) {
}
3.2 إذا كنت تستخدم TreeMap
1. عند اجتياز المفتاح والقيمة في نفس الوقت، على عكس HashMap، يكون أداءentrySet أعلى بكثير من أداء keySet. يتم تحديد ذلك من خلال كفاءة الاستعلام في TreeMap، بمعنى آخر، تكلفة البحث عن القيمة في TreeMap كبيرة نسبيًا، وهي أعلى بكثير من تكلفة استرداد جميع المفاتيح والقيم مرة واحدة منentrySet. لذلك، يوصى بشدة باستخدام طريقة الإدخال عند اجتياز TreeMap.
2. عند اجتياز المفاتيح فقط، تكون طريقة keySet أكثر ملاءمة، لأنentrySet تحذف أيضًا قيمًا غير مفيدة، مما يهدر الأداء والمساحة. في نتائج الاختبار المذكورة أعلاه، يستغرق keySet وقتًا أقل بنسبة 24% من طريقة الإدخال.
3. عند اجتياز القيمة فقط، فإن استخدام طريقة vlaues هو الخيار الأفضل، ومن الواضح أنentrySet أفضل أيضًا من طريقة keySet.
4. من بين طرق الكتابة المختلفة، يوصى باستخدام طريقة الكتابة التالية، وهي أكثر كفاءة قليلاً:
انسخ رمز الكود كما يلي:
لـ (مفتاح السلسلة: Map.keySet()) {
القيمة = Map.get(key);
}
لـ (إدخال <سلسلة، سلسلة>: Map.entrySet ()) {
مفتاح = دخول.getKey();
القيمة = الإدخال.getValue();
}
لـ (قيمة السلسلة: Map.values()) {
}