لقد رأيت مؤخرًا وصفًا جيدًا جدًا لإطار عمل المجموعة في كتاب J2EE وقمت بتصفيته ونشره لمشاركته مع الجميع. يوفر إطار عمل المجموعة واجهات وفئات لإدارة مجموعات الكائنات فيما يلي وصف لكل مكون.
واجهة التجميع
المجموعة هي واجهة المجموعة الأساسية. تمثل المجموعة مجموعة من الكائنات، أي عناصر المجموعة. تسمح بعض المجموعات بعناصر متطابقة والبعض الآخر لا يسمح بذلك. بعض النوع والبعض الآخر لا. لا توفر Java SDK الفئات التي ترث مباشرة من المجموعة. الفئات التي توفرها Java SDK كلها "واجهات فرعية" ترث من المجموعة، مثل القائمة والمجموعة.
يجب أن توفر جميع الفئات التي تنفذ واجهة المجموعة مُنشئين قياسيين: مُنشئ بدون معلمات لإنشاء مجموعة فارغة، ومنشئ معلمة مجموعة لإنشاء مجموعة جديدة. تحتوي مجموعة الإدخال على نفس العناصر. يسمح المنشئ الأخير للمستخدم بنسخ المجموعة.
كيفية التكرار من خلال كل عنصر في المجموعة؟ بغض النظر عن النوع الفعلي للمجموعة، فهي تدعم طريقة iterator()، والتي تُرجع مكررًا يمكن استخدامه للوصول إلى كل عنصر في المجموعة واحدًا تلو الآخر. الاستخدام النموذجي هو كما يلي:
انسخ رمز الكود كما يلي:
Iterator it = Collection.iterator(); // احصل على مكرر
بينما(it.hasNext()) {
Object obj = it.next(); // احصل على العنصر التالي
}
الواجهتان المشتقتان من واجهة المجموعة هما List وSet.
واجهة القائمة القائمة عبارة عن مجموعة مرتبة باستخدام هذه الواجهة، يمكنك التحكم بدقة في موضع الإدراج لكل عنصر. يمكن للمستخدمين الوصول إلى العناصر الموجودة في القائمة باستخدام الفهرس (موضع العنصر في القائمة، يشبه مصفوفة منخفضة)، والذي يشبه مصفوفة Java.
على عكس المجموعة المذكورة أدناه، تسمح القائمة بنفس العناصر.
بالإضافة إلى طريقة iterator() الضرورية لواجهة المجموعة، توفر List أيضًا طريقة listIterator()، والتي تُرجع واجهة ListIterator. وبالمقارنة مع واجهة Iterator القياسية، يحتوي ListIterator على المزيد من الإضافات () وطرق أخرى، مما يسمح بالإضافات. قم بحذف العناصر وتعيينها والانتقال للأمام أو للخلف.
الفئات الشائعة التي تنفذ واجهة القائمة هي LinkedList وArrayList وVector وStack.
فئة القائمة المرتبطة يقوم LinkedList بتنفيذ واجهة القائمة ويسمح بالعناصر الفارغة. بالإضافة إلى ذلك، يوفر LinkedList طرقًا إضافية للحصول على وإزالة وإدراج في رأس LinkedList أو ذيله. تسمح هذه العمليات باستخدام LinkedList كمكدس أو قائمة انتظار أو deque.
لاحظ أن LinkedList لا يحتوي على طرق متزامنة. إذا وصلت عدة سلاسل رسائل إلى القائمة في نفس الوقت، فيجب عليها تنفيذ مزامنة الوصول بنفسها. أحد الحلول هو إنشاء قائمة متزامنة عند إنشاء القائمة:
قائمة القائمة = Collections.synchronizedList(new LinkedList(...));
فئة قائمة المصفوفات تطبق ArrayList المصفوفات ذات الحجم المتغير. يسمح لجميع العناصر، بما في ذلك فارغة. لم تتم مزامنة ArrayList.
وقت تشغيل أساليب الحجم، isEmpty، get، وset ثابت. ومع ذلك، فإن تكلفة طريقة الإضافة هي ثابت مطفأ، وإضافة n من العناصر يتطلب وقت O(n). الطرق الأخرى لها وقت تشغيل خطي.
يحتوي كل مثيل ArrayList على سعة (Capacity)، وهي حجم المصفوفة المستخدمة لتخزين العناصر. وتزداد هذه السعة تلقائيًا عند إضافة عناصر جديدة، ولكن لم يتم تحديد خوارزمية النمو. عندما يلزم إدراج عدد كبير من العناصر، يمكن استدعاء طريقة ضمان السعة لزيادة سعة ArrayList قبل الإدراج لتحسين كفاءة الإدراج.
مثل LinkedList، فإن ArrayList غير متزامن أيضًا.
فئة المتجهات Vector مشابه جدًا لـ ArrayList، لكن Vector متزامن. على الرغم من أن التكرار الذي تم إنشاؤه بواسطة Vector له نفس واجهة التكرار الذي تم إنشاؤه بواسطة ArrayList، نظرًا لمزامنة Vector، عند إنشاء Iterator واستخدامه، يغير مؤشر ترابط آخر حالة Vector (على سبيل المثال، إضافة أو إزالة بعض العناصر) سيتم طرح ConcurrentModificationException عند استدعاء أسلوب Iterator، لذلك يجب اكتشاف الاستثناء.
فئة المكدس يرث المكدس من Vector وينفذ مكدس ما يدخل أولاً يخرج أولاً. يوفر Stack 5 طرق إضافية تسمح باستخدام Vector كمكدس. تعمل طرق الدفع والبوب الأساسية، بالإضافة إلى طريقة النظرة الخاطفة، على الحصول على العنصر الموجود أعلى المكدس، وتختبر الطريقة الفارغة ما إذا كانت المكدس فارغة، وتكتشف طريقة البحث موضع العنصر في المكدس. المكدس عبارة عن مكدس فارغ بعد إنشائه.
ضبط الواجهة Set عبارة عن مجموعة لا تحتوي على عناصر مكررة، أي أن أي عنصرين e1 وe2 لهما e1.equals(e2)=false، وتحتوي Set على عنصر فارغ واحد على الأكثر.
من الواضح أن مُنشئ المجموعة لديه قيد يتمثل في أن معلمة المجموعة التي تم تمريرها لا يمكن أن تحتوي على عناصر مكررة.
يرجى ملاحظة: يجب التعامل مع الكائنات القابلة للتغيير بعناية. إذا قام عنصر قابل للتغيير في مجموعة بتغيير حالته مما تسبب في Object.equals(Object)=true، فسوف يسبب بعض المشاكل.
واجهة الخريطة <BR> يرجى ملاحظة أن الخريطة لا ترث واجهة المجموعة، وتوفر الخريطة مفتاحًا لتعيين القيمة. لا يمكن أن تحتوي الخريطة على نفس المفتاح، ويمكن لكل مفتاح تعيين قيمة واحدة فقط. توفر واجهة الخريطة ثلاثة أنواع من طرق عرض المجموعة. يمكن اعتبار محتوى الخريطة مجموعة من مجموعات المفاتيح، أو مجموعة من مجموعات القيم، أو مجموعة من تعيينات القيمة الرئيسية.
فئة هاشتابل يرث Hashtable واجهة الخريطة وينفذ جدول تجزئة لتعيين قيمة المفتاح. يمكن استخدام أي كائن غير فارغ كمفتاح أو قيمة.
لإضافة البيانات، استخدم put(key, value)، ولإزالة البيانات، استخدم get(key). تكلفة الوقت لهاتين العمليتين الأساسيتين ثابتة.
يقوم Hashtable بضبط الأداء من خلال معلمتين: السعة الأولية وعامل الحمولة. عادةً ما يحقق عامل التحميل الافتراضي 0.75 توازنًا أفضل بين الزمان والمكان. يمكن أن تؤدي زيادة عامل التحميل إلى توفير المساحة ولكن سيزيد وقت البحث المقابل، مما سيؤثر على عمليات مثل الحصول والوضع.
مثال بسيط لاستخدام Hashtable هو كما يلي. ضع 1 و2 و3 في Hashtable، ومفاتيحها هي "واحد" و"اثنان" و"ثلاثة" على التوالي:
انسخ رمز الكود كما يلي:
أرقام جدول التجزئة = جدول التجزئة الجديد () ؛
أرقام.put("واحد"، عدد صحيح جديد(1));
number.put("two", new Integer(2));
number.put("ثلاثة", عدد صحيح جديد(3));
لاسترداد رقم، مثل 2، استخدم المفتاح المقابل:
عدد صحيح n = (عدد صحيح)numbers.get("two");
System.out.println("two =" + n);
نظرًا لأن الكائن المستخدم كمفتاح سيحدد موضع القيمة المقابلة عن طريق حساب دالة التجزئة الخاصة به، فإن أي كائن يستخدم كمفتاح يجب أن يطبق طريقتي hashCode وequal. ترث أساليب hashCode وequals من كائن الفئة الجذر. إذا كنت تستخدم فئة مخصصة كمفتاح، فكن حذرًا للغاية وفقًا لتعريف دالة التجزئة، إذا كان الكائنان متماثلان، أي obj1.equals(. obj2)=true، فيجب أن يكون رمز التجزئة الخاص بهم نفس الشيء، ولكن إذا كان هناك كائنان مختلفان، فإن رمز التجزئة الخاص بهما ليس بالضرورة مختلفًا. إذا كان رمز التجزئة لكائنين مختلفين هو نفسه، فإن هذه الظاهرة تسمى تعارضًا، مما يؤدي إلى زيادة التكلفة الزمنية لتشغيل جدول التجزئة لتعريفه جيدًا، يمكن لطريقة hashCode() تسريع عمليات جدول التجزئة.
إذا كان الكائن نفسه يحتوي على كود تجزئة مختلف، فإن تشغيل جدول التجزئة سيكون له نتائج غير متوقعة (ترجع طريقة الحصول المتوقعة قيمة فارغة). لتجنب هذه المشكلة، ما عليك سوى تذكر شيء واحد: تجاوز طريقة يساوي وطريقة hashCode في نفس الوقت. الوقت لا تكتب واحد منهم فقط.
Hashtable متزامن.
فئة HashMap يشبه HashMap Hashtable، باستثناء أن HashMap غير متزامن ويسمح بالقيمة الخالية، أي القيمة الخالية والمفتاح الفارغ. ، ولكن عند التعامل مع HashMap كمجموعة (يمكن لأسلوب القيم () إرجاع مجموعة)، فإن الحمل الزمني للعمليات الفرعية التكرارية يتناسب مع سعة HashMap. لذلك، إذا كان أداء العمليات التكرارية مهمًا جدًا، فلا تقم بتعيين السعة الأولية لـ HashMap على مستوى عالٍ جدًا أو عامل التحميل على مستوى منخفض جدًا.
فئة WeakHashMap WeakHashMap عبارة عن HashMap محسّن ينفذ "مراجع ضعيفة" للمفاتيح إذا لم يعد يتم الرجوع إلى المفتاح خارجيًا، فيمكن إعادة تدوير المفتاح بواسطة GC.
ملخص <BR>إذا كانت هناك عمليات مثل المكدسات وقوائم الانتظار، فيجب عليك التفكير في استخدام القائمة. إذا كنت بحاجة إلى إدراج العناصر وحذفها بسرعة، فيجب عليك استخدام LinkedList. إذا كنت بحاجة إلى وصول عشوائي سريع إلى العناصر، فيجب عليك استخدام ArrayList.
إذا كان البرنامج في بيئة ذات ترابط واحد، أو تم الوصول إليه فقط في مؤشر ترابط واحد، ففكر في الفئات غير المتزامنة، والتي تكون أكثر كفاءة. إذا كانت مؤشرات الترابط المتعددة يمكنها تشغيل فئة في نفس الوقت، فيجب استخدام الفئات المتزامنة.
انتبه بشكل خاص إلى تشغيل جدول التجزئة. يجب أن يتجاوز الكائن المستخدم كمفتاح أساليب يساوي ورمز التجزئة بشكل صحيح.
حاول إرجاع الواجهة بدلاً من النوع الفعلي، مثل إرجاع القائمة بدلاً من ArrayList، بحيث إذا كنت بحاجة إلى استبدال ArrayList بـ LinkedList في المستقبل، فلن يحتاج رمز العميل إلى التغيير. هذه برمجة للتجريد.