السلام عليكم و رحمة الله و بركاته
البداية ستكون مع تعريف بسيط و مختصر لهياكل البيانات بشكل عام ثم نأتي بعد ذلك إلى الـ Collections و أهميتها في البرمجة لننتقل إلى التغيرات الجديدة التي أضافها الـ Collections FrameWork من خلال استعراض الـ Hierarchy الجديد و شرح دور مختلف الـ interfaces الموجودة مثل Map, Set, List, SortedSet, SotedMap و الـ Iterators المرافقة لكل interface بالإضافة إلى شرح مفصل لمختلف الدوال الموجودة في كل واحدة مع أمثلة في نهاية كل فقرة.
بعد ذلك ننتقل إلى الـ views و أنواعها الموجودة و كيفية استخدامها.
الفقرة الموالية تتعلق بترتيب الـ Collections مع توضيح الفرق بين Comparable و Comparator و متى نستخدم كل واحدة منهما مع مثال توضيحي شامل.
الختام سيكون مع الـ algorithmes التي تُمكن من التعامل مع الـ collections من خلال وصف دقيق لكل خورارزمية على حدة بالإضافة إلى كيفية الحصول على نسخة multi-thread أو unmodifiable لـ list معينة
تعريف مختصر لهياكل البيانات
عبارة عن طريقة خاصة لتخزين وتنظيم البيانات على شكل array, list, stack .. etc, هذه الخصوصية تكمن في كمية الذاكرة المستخدمة لتخزين البيانات بالإضافة إلى الوقت المستغرق لإجراء بعض العمليات على تلك البيانات. تختلف أنواع هياكل البيانات باختلاف التطبيقات البرمجية أو نوعية الاستخدام, كل حسب ما يناسبه. فمثلا نجد أن الــ B-Tree تناسب التطبيقات الخاصة بقواعد البيانات بينما نجد الــ Hash Table يُستخدم بكثرة في برمجة الــ compilers خصوصا عند البحث عن الــ identifiers.
ما هي الــ Collections ؟
تقوم الــ collections بإدارة و تسيير مجموعة من الكائنات ذات نوع معين أو يمكننا القول بأنها عبارة عن كائن يمكنه تخزين بيانات من نوع آخر. في أولى إصدارات الجافا, اقتصر تمثيل الــ collections في Vector, Stack, Hashtable, Bitset ثم ظهر بعد ذلك الــ Collections frameWork في Java 1.2 الذي حافظ على المبادئ الأساسية لهياكل البيانات و أضاف العديد من التعديلات و التحسينات بحيث تم تصميم تلك الهياكل على شكل شجري محكم التنسيق.
يمكن للــ collections تخزين العناصر أيا كان نوعها بشرط أن تكون كائنات. يمكن مثلا أن تكون String, Integer, Float, Manager أو أي كائن آخر .. لكن مع ذلك من الخطر جدا استخدام collection تحتوي على أنواع مختلفة من الكائنات لأنك ستحتاج دوما إلى استخدام الــ casting أو instanceof من أجل تحديد صنف الكائن. بالتالي يُستحسن دائما تخزين كائنات من نفس النوع في الــ collection الواحدة.
عند إدراج عنصر معين في الــ collection يتم الاكتفاء بتخزين الــ reference الخاص بالــ object و بالتالي يمكننا أحيانا إدراج الــ null reference داخل collection.
مالجديد في Collections FrameWork ؟
يحتوي الإصدار الجديد على العديد من الفئات و الواجهات كما يُقدم أيضا بعض الــ abstract classes التي تقوم بعمل implementation لبعض الــ interfaces.
الواجهات التي يمكن استخدامها مع كائنات للتعامل مع الــ collections هي :
- Collection : معظم الفئات التي تتعامل مع الــ collections قامت بعمل implement لهذه الواجهة.
- Map : هذه الواجهة تحتوي على الدوال أو الــ methods الخاصة بالكائنات التي تتعامل مع الــ associative array على شكل key/value.
- Set : خاصة بالكائنات التي لا تقبل التكرار داخل المجموعة.
- List : الكائنات التي تقبل التكرار داخل المجموعة و تدعم الوصول إلى العناصر بشكل مباشر.
- SortedSet : هذه الواجهة ترث الواجهة Set و تسمح بترتيب العناصر.
- SortedMap : ترث الواجهة Map و تسمح بترتيب العناصر.
بعض الدوال (اعذرني أخي القارئ فأنا أفضل كلمة الدوال على مصطلح الطرق) الموجودة في الواجهات السابقة, اختيارية بمعنى أن تعريفها اجباري لكن إذا كانت العملية غير مدعومة فيمكن للدالة أن تُصدر exception تُوضح نوع خطأ الاستخدام. الهدف من شيء كهذا هو تقليل عدد الواجهات و تلبية أكبر عدد ممكن من الحاجات المطلوبة.
أيضا, الــ framework يوفر العديد من الكائنات التي تدعم الواجهات السابقة و يمكن استخدامها بشكل مباشر:
- HashSet : عبارة عن Hash table أو جدول هاش يدعم الواجهة Set.
- TreeSet : شجرة (tree) قامت بعمل implement للواجهة SortedSet.
- ArrayList : مصفوفة ديناميكية قامت بعمل implement للواجهة List.
- LinkedList : قائمة متصلة مزدوجة قامت بعمل implement للواجهة List.
- HashMap : جدول هاش (Hash table) قام بعمل implement للواجهة Map.
- TreeMap : شجرة (tree) قامت بعمل implement للواجهة SortedMap.
بالنسبة للواجهات التي تُمكن من المرور على عناصر الــ collections و ترتيبها فهي :
- Iterator : للتجول بين مختلف عناصر الــ collection.
- ListIterator : تسمح بالمرور على عناصر List في الاتجاهين كما تسمح بتغيير عناصر المجموعة.
- Comparable : لتعريف نوع الترتيب الطبيعي لكائن معين.
- Comparator : لتعريف ترتيب كيفي (لا على التعيين).
هذا بالإضافة إلى فئتين كانتا توجدان في النسخة السابقة و أجريت عليهما بعض التعديلات لدعم بعض الواجهات الجديدة:
- Vector : مصفوفة ديناميكية قامت بعمل implement للواجهة الجديدة List
- HashTable : Hash table قام بعمل implement للواجهة الجديدة Map
المخطط الجديد لــ Collections FrameWork
1 – Collection Hierarchy
1.1 – الواجهة Collection
public interface Collection { // Basic Operations int size(); boolean isEmpty(); boolean contains(Object element); boolean add(Object element); // Optional boolean remove(Object element); // Optional Iterator iterator(); int hashCode(); boolean equals(Object element); // Bulk Operations boolean containsAll(Collection c); boolean addAll(Collection c); // Optional boolean removeAll(Collection c); // Optional boolean retainAll(Collection c); // Optional void clear(); // Optional // Array Operations Object[] toArray(); Object[] toArray(Object a[]); }
كما قلنا سابقا, أغلب الواجهات الجديدة تحتوي على دوال اختيارية, هذه الخاصية تسمح لك بالتعامل مع هياكل البيانات الخاصة دون أن تحتاج إلى تعريف دوال جديدة. الدوال الاختيارية تُستعمل عند الحاجة فقط. على سبيل المثال إذا كان لدينا Set غير قابلة للتعديل يمكننا إعادة تعريف الدالة add بحيث نمنع التعديل أو الإضافة. تختلف الدوال باختلاف مهامها فهناك بعض الدوال ينفذ العملية بشكل فردي (أي على كائن واحد) و هناك أخرى تنفذها بشكل جماعي أي على مجموعة كائنات في آن واحد.
- الدالتان add و remove تسمحان بإضافة أو حذف عنصر معين بينما تقوم addAll أو removeAll بحذف أو إضافة مجموعة كائنات بشكل جماعي.
- الدالة contains تتحقق من وجود عنصر داخل المجموعة. إذا أردنا التحقق من وجود مجموعة عناصر في آن واحد نستخدام containsAll.
- size, isEmpty و clear يمكنهم (على التوالي) معرفة حجم الــ collection, التحقق من أن الــ collection فارغة و الأخيرة تُمكن من حذف محتوى الــ collection.
- retainAll تُستعمل لإيجاد تقاطع الــ collections, فمثلا إذا كانت A={1,2,5,8} و B={3,8} فإن A ستصبح {8}.
- equals تتحقق من تطابق الــ collections.
- hashCode تُعيد كود الــ hash code الخاص بالــ collection.
- toArray تُعيد عناصر الــ collection على شكل مصفوفة.
- toArray(Object a[])l تسمح بتحديد نوع المصفوفة المعادة.
للمرور على عناصر collection نستخدم الواجهة Iterator :
public interface Iterator { boolean hasNext(); Object next(); void remove(); // Optional }
- hasNext : تتحقق من وجود عنصر قادم.
- next : تُشير إلى العنصر القادم.
- remove : تحذف العنصر الحالي.
ملاحظة:
الدالة next تُصدر NoSuchElementException عند استدعائها بعد انتهاء عناصر الــ collection. لتفادي هذه الـــ Exception يكفي تطبيق hasNext على الكائن المُعاد من طرف الدالة next, هكذا :
Iterator iter = objetCollection.iterator(); while (iter. hasNext()) { Object o = iter.next() ; System.out.println ("Objet : " + o); }
1.2 – الواجهة Set
هذه الواجهة عبارة عن Collection غير مرتبة, لا تسمح بتكرار العناصر. يوجد نوعان من الــ implementation, الاختيار بينهما يكون حسب ضرورة ترتيب العناصر:
- HashSet : العناصر غير مرتبة, عملية الإضافة سريعة جدا.
- TreeSet : العناصر مرتبة, عملية الإضافة تكون ابطأ.
مالفرق بين الاثنين ؟
- HashSet : تستخدم تقنية الــ hashing لتحديد تكرار العنصر و تعتمد على الدالة hashCode في الترتيب.
- TreeSet : تستخدم binary tree لترتيب العناصر و تعتمد على الدالة compareTo في الترتيب.
ملاحظات:
- نوع الترتيب المُستخدم يتم تحديده من طرف الكائنات المضافة إلى TreeSet من خلال عمل implementation للواجهة Comparable إذا أردنا الترتيب حسب natural sort order أو يمكننا تمرير كائن من Comparator لدالة بناء TreeSet إذا أردنا تحديد الــ sort order.
- للتأكد من وجود عنصر معين داخل Set استخدم الدالة equals. مع العلم طبعا بأنه يجب إعادة تعريف equals حتى تستطيع التأكد من وحدانية أو عدم تكرار الكائن المضاف.
انظر المثال:
import java.util.HashSet; import java.util.Set; import java.util.TreeSet; public class SetExample { public static void main(String args[]) { Set set = new HashSet(); // Une table de Hachage set.add("Bernadine"); set.add("Elizabeth"); set.add("Gene"); set.add("Elizabeth"); set.add("Clara"); System.out.println(set); Set SetTrie = new TreeSet(set); // Un Set trié System.out.println(SetTrie); } }
1.3 – الواجهة List
List عبارة عن collection مرتبة تسمح بتكرار العناصر, تحتوي هذه الواجهة على دوال تُمكن من إضافة أو حذف عنصر في أي مكان من المجموعة. كما تسمح بالتعامل مع القوائم المتداخلة, في أغلب الحالات نستخدم ArrayList إلا إذا كانت هناك عمليات إضافة في المنتصف, عندها يُستحسن استخدام LinkedList لتفادي عمليات الإزاحة المتكررة.
public interface List extends Collection { // Positional Access Object get(int index); Object set(int index, Object element); // Optional void add(int index, Object element); // Optional Object remove(int index); // Optional boolean addAll(int index, Collection c); // Optional // Search int indexOf(Object o); int lastIndexOf(Object o); // Iteration ListIterator listIterator(); ListIterator listIterator(int index); // Range-view List subList(int fromIndex, int toIndex); }
دوال هذه الواجهة تسمح بإجراء بعض العمليات على عنصر يتم تحديد موقعه أو على مجموعة عناصر ابتداء من index معين.
- الدالة get تُعيد العنصر الموجود في الموقع index.
- set تُغير العنصر الموجود في الموقع index.
- indexOf (على التوالي lastIndexOf) تبحث عن وجود العنصر في المجموعة و تُعيد رقم موقعه (على التوالي رقم آخر ظهور له) في المجموعة.
- subList تسمح بإنشاء قائمة داخل أخرى.
للمرور على عناصر List تم إنشاء Iterator خاص :
public interface ListIterator extends Iterator { boolean hasNext(); Object next(); boolean hasPrevious(); Object previous(); int nextIndex(); int previousIndex(); void remove(); // Optional void set(Object o); // Optional void add(Object o); // Optional }
حيث يسمح بالمرور على عناصر List في الاتجاهين بالإضافة إلى إمكانية تغيير عنصر معين أو إضافة آخر جديد :
List list = ...; ListIterator iterator = list.listIterator(list.size()); while (iterator.hasPrevious()) { Object element = iterator.previous(); // traitement d'un élément }
nextIndex تُعيد رقم موقع العنصر الحالي و بقية الدوال رأيناها سابقاً.
1.4 – الكلاس ArrayList
هذا الكلاس يرث الكلاس AbstractList مما يعني أنه قام بعمل implement للواجهة List يُمثل مصفوفة ديناميكية من الكائنات. طريقة عمله مطابقة تماما لتلك الخاصة بــ Vector, الفرق بينهما هو أن هذا الأخير يدعم الــ multi-thread (جميع الدوال synchronized). اذا اردنا استخدام thread واحد فالــ synchronization تكون مكلفة و عديمة الفائدة لذا من الأفضل استخدام ArrayList في مثل هذه الحالات.
اضافة إلى الدوال السابقة توجد:
- ensureCapacity : تسمح بزيادة حجم المصفوفة لضمان تخزين العناصر الجديدة.
- trimToSize : تسوية سعة المصفوفة على حجمها الحالي.
مثال أول:
import java.util.ArrayList; public class TestArrayList01 { public static void main(String[] args) { ArrayList c = new ArrayList(); /* * ajout de dix element de type Integer */ for (int i = 0; i < 10; i++) { c.add(new Integer(i)); } /* * affiche des element par accès direct */ for (int i = 0; i < c.size(); i++) { System.out.print(c.get(i) + " ");//1 } System.out.println(); /* * suppression d'element aux positions indiquées */ c.remove(2); c.remove(4); c.remove(6); //c.remove(8) ; /* * affiche de la liste */ System.out.print(c.toString());//2 /* * ajout de dix element de type Integer */ c.add(2, "100"); c.add(2, "200"); c.add(5, "300"); /* * réaffiche de la liste */ System.out.print(c.toString());//3 } }
مُخرجات الكود ستكون بهذا الشكل:
بعد حذف 2, 5 و 8 سيكون طول المصفوفة يساوي 7 و بالتالي عندما نحاول حذف العنصر الثامن سيتم رفع استثناء من نوع IndexOutOfBoundsException.
مثال ثاني:
import java.util.ArrayList; import java.util.ListIterator; public class TestArrayList02 { public static void main(String[] args) { ArrayList c = new ArrayList(); /* * ajout de dix element de type Integer */ for (int i = 0; i < 10; i++) { c.add(new Integer(i)); } /* * affiche toute la liste */ System.out.println(c.toString()); /* * iterateur sur le tableau */ ListIterator iter = c.listIterator(); while (iter.hasNext()) { Integer k = (Integer) iter.next(); int i = k.intValue(); if (i % 2 == 0) { iter.set(new Integer(500)); } } /* * affiche toute la liste */ System.out.print(c.toString()); } }
مُخرجات الكود ستكون على النحول التالي:
2 – Map Hierarchy
2.1 – ماهو الــ Map ؟
الــ Map عبارة عن مجموعة أزواج, كل زوج يحتوي على مفتاح و قيمة key/value مع العلم أن المفتاح لا يمكن تكراره. (في الحقيقة يمكننا اسناد عدة قيم لمفتاح واحد, في هذه الحالة سنكون أمام multimap). الهدف الأساسي من الــ Map هو الوصول بسرعة إلى المفتاح للحصول على القيمة المقابلة له.
public interface Map { // Basic Operations Object put(Object key, Object value); Object get(Object key); Object remove(Object key); boolean containsKey(Object key); boolean containsValue(Object value); int size(); boolean isEmpty(); // Bulk Operations void putAll(Map t); void clear(); // Collection Views public Set keySet(); public Collection values(); public Set entrySet(); // Interface for entrySet elements public interface Entry { Object getKey(); Object getValue(); Object setValue(Object value); } }
2.2 – عرض سريع لمختلف الدوال الموجودة داخل الواجهة Map
- الدالة clear تسمح بحذف جميع عناصر الــ Map.
- containsKey تُحدد ما إذا كان المفتاح key موجود أو لا.
- containsValue تُحدد ما إذا كانت القيمة value موجودة داخل الــ Map.
- entrySet تُعيد Set تحتوي على جميع قيم الــ Map.
- get تُعيد القيمة المرافقة للمفتاح key.
- isEmpty تتحق من فراغ الــ Map.
- keySet تُعيد Set تحتوي على كافة مفاتيح الــ Map.
- put تضيف المفتاح key و القيمة المرافقة له value داخل الــ Map.
- putAll تضيف كافة المفاتيح و القيم داخل الــ Map.
- values تُعيد قيم الــ Map على شكل collection.
- remove يحذف العنصر المقابل للمفتاح key.
- size تُعيد عدد عناصر الــ Map.
2.3 – ماهي انواع الــ implementation الموجودة ؟
يوجد نوعان من الــ Map :
- HashMap, Hashtable : يستخدمان جدول هاش.
- TreeMap : تستخدم شجرة ثنائية.
ملاحظات :
- الفئة HashMap ليست synchronized. للتعامل مع الــ concurrent access, قم بتغليف الكائن داخل كائن من Map باستخدام الدالة synchronizedMap الموجودة في الواجهة Collection.
- إذا كان المفتاح المُمرر إلى put موجود مسبقا سيتم استبدال القيمة القديمة بالجديدة. put تُعيد القيمة القديمة إذا كان المفتاح موجود مسبقا و إلا فستعيد null.
- TreeMap قامت بعمل implement لــ SortedMap, ترتيب العناصر يتم تحديده عن طريق كائن من نوع Comparable.
2.4 – ماهي الــ view ؟
- تسمح الــ view بالمرور على عناصر الـــ Map.
- نظريا, الفئتان HashMap و TreeMap لا يملكان أي Iterator.
- الدالة entrySet تسمح برؤية Map كمجموعة ازواج.
- الزوج عبارة عن كائن من نوع الواجهة Map.entry حيث يجمع كائنين في آن واحد.
- الدالتان getKey و getValue الموجودتان في Map.entry تسمحان باستخراج مفتاح و قيمة زوج معين.
انظر المثال:
import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class TestHashMapView { public static void main(String[] args) { HashMap table = new HashMap(); /* * ajout d'éléments dans la table */ table.put(new Integer(1), "Livre Java"); table.put(new Integer(2), "Livre Oracle"); table.put(new Integer(3), "Livre C++"); table.put(new Integer(4), "Livre Reseaux"); Set entrees = table.entrySet();// entrees est un ensemble de paires Iterator iter = entrees.iterator(); // un iterateur sur les paires while (iter.hasNext()) { Map.Entry entree = (Map.Entry) iter.next(); // on est sur la paire courante Object cle = entree.getKey(); Object valeur = entree.getValue(); if (cle != null && cle.toString().equals(new Integer(2).toString())) { table.put(new Integer(2), "Revue Technical Report"); } } System.out.println(table.get(new Integer(2))); } }
بالإضافة إلى الــ view السابقة توجد أخرى, للحصول على كافة المفاتيح نستخدم الدالة keySet :
import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class ExampleKeySet { public static void main(String[] args) { HashMap map = new HashMap(); map.put("1", "stock"); map.put("2", "livres"); map.put("3", "sucre"); map.put("4", "huile"); //ensemble des cles Set cles = map.entrySet(); System.out.println("Aff1 " + cles); Iterator iter = cles.iterator(); while (iter.hasNext()) { Map.Entry entree = (Map.Entry) iter.next(); String s = (String) entree.getValue(); if (s.equals("huile")) { entree.setValue("riz"); } System.out.println("Element courant " + s); } System.out.println("Affiche " + cles); } }
للحصول على كافة القيم نستخدم الدالة values :
import java.util.Collection; import java.util.HashMap; import java.util.Iterator; public class ExampleValues { public static void main(String[] args) { HashMap map = new HashMap(); map.put("1", "stock"); map.put("2", "livres"); map.put("3", "sucre"); map.put("4", "huile"); //collection des valeurs Collection valeurs = map.values(); System.out.println("Affichage 1 " + valeurs); Iterator iter = valeurs.iterator(); while (iter.hasNext()) { Object o = iter.next(); if (o.toString().equals("huile")) { iter.remove(); } System.out.println("Element courant " + o); } System.out.println("Affichage 2 " + valeurs); } }
ملاحظات:
- أي تغيير يحدث في الـــ Map سيتم اجراؤه أيضا على الــ view المقابلة.
- حذف الزوج أو العنصر الحالي (باستخدام remove) في الــ view يقتضي أيضا حذف العنصر المقابل له في الــ Map.
- لا يُسمح بإضافة عناصر مباشر من داخل الــ view.
مثال جامع :
ليكن لدينا الكلاس التالي:
public class Produit { private String idProduit, libelle, quantite; //نفترض أن الكلاس يحتوي على دالة بناء بالإضافة إلى //getters and setters }
نُعطي الكلاس EnregistrementProduit المعرف كما يلي:
import java.util.ArrayList; import java.util.HashMap; public class EnregistrementProduit { private static HashMap<String, Produit> prod = new HashMap<String, Produit>(); public static HashMap<String, Produit> enreg(Produit e) { //أكمل الكود } public static ArrayList<Produit> chercherProduits(HashMap<String, Produit> prod) { //أكمل الكود } }
- قم بتعريف الدالة enreg بحيث تقوم بتخزين الــ Produit الذي تم تمريره كوسيط داخل جدول الهاش prod. نعتبر أن idProduit هو المفتاح.
- قم أيضا بتعريف الدالة chercherProduits بحيث تقوم بإعادة مجموعة الــ Products ذو quantite أقل من 15000.
الحل:
import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; class Produit { private String idProduit, libelle, quantite; public Produit(String id, String libel, String qnt) { idProduit = id; libelle = libel; quantite = qnt; } public String getId() { return idProduit; } public String getQnt() { return quantite; } } public class EnregistrementProduit { private static HashMap<String, Produit> prod = new HashMap<String, Produit>(); public static HashMap<String, Produit> enreg(Produit p) { prod.put(p.getId(), p); return prod; } public static ArrayList<Produit> chercherProduits(HashMap<String, Produit> prod) { Collection c = prod.values(); ArrayList<Produit> array = new ArrayList<Produit>(); Iterator<Produit> iter = c.iterator(); while (iter.hasNext()) { Produit p = iter.next(); if (Integer.parseInt(p.getQnt()) > 15000) { array.add(p); } } return array; } }
ترتيب الــ Collections
يتم تحديد نوع الترتيب المُستخدم في الــ Collections باستخدام احدى الواجهتين :
Comparable Or Comparator
1 – الواجهة java.lang.Comparable
جميع الكائنات التي تستخدم natural sort order يجب أن تقوم بعمل implement لهذه الواجهة:
interface Comparable { int compareTo(Object o); }
بعض الفئات مثل String, File, Date, Integer, Float الخ, عملت implement لهذه الواجهة.
Comparable تحتوي على دالة واحدة فقط هي compareTo التي تعيد احدى القيم التالية:
- قيمة اكبر من الصفر, إذا كان الكائن الحالي (current object) اكبر من الكائن الذي تم تمريره.
- اصغر من الصفر, إذا كان الكائن الحالي (current object) اصغر من الكائن الذي تم تمريره.
- صفر, اذا تطابق الكائنان.
مثال:
قم بكتابة كلاس باسم TestComparable يقوم بعمل implement للواجهة Comparable و يسمح بترتيب عناصر موجودة في HashMap على النحو التالي:
- الدالة void tri (Object [ ]o)l تسمح بترتيب جدول من Object.
- الدالة ArrayList triHashMap (HashMap map)l تستقبل HashMap كوسيط و تُعيد ArrayList يحتوي على قيم الــ HashMap بشكل مرتب.
- مع العلم أنه يجب اعادة تعريف الدالة CompareTo حتى توافق المتطلبات.
الحل:
import java.util.*; public class TestComparable implements Comparable { public int compareTo(Object o) { if (o instanceof String) { String d = (String) o; return this.compareTo(d); } else { new ClassCastException(); return -1; } } ArrayList triHashMap(HashMap map) { int i = 0; ArrayList liste = new ArrayList(); Set c = map.entrySet(); Object tab[] = new Object[c.size()]; Iterator it = c.iterator(); Map.Entry mp = null; while (it.hasNext()) { mp = (Map.Entry) it.next(); tab[ i] = mp.getValue(); i++; } tri(tab); for (int ii = 0; ii < tab.length; ii++) { liste.add(tab[ ii]); } return liste; } public void tri(Object[] tab) { for (int i = 0; i < tab.length - 1; i++) { if (tab[i].toString().compareTo(tab[i + 1].toString()) > 0) { String temp = tab[i].toString(); tab[i] = tab[i + 1]; tab[i + 1] = temp; tri(tab); } } } public static void main(String[] args) { TestComparable tt = new TestComparable(); HashMap map = new HashMap(); map.put("1", "stock"); map.put("2", "livres"); map.put("3", "sucre"); map.put("4", "huile"); map.put("5", "riz"); map.put("6", "or"); Collection val = map.values(); System.out.println("aff 1 " + val); ArrayList h = tt.triHashMap(map); System.out.println("aff 2 " + h.toString()); } }
2 – الواجهة java.util.Comparator
هذه الواجهة تُمثل sort order كيفي, أي أن المبرمج هو من يحدده. حيث تسمح بترتيب الكائنات التي لم تقم بعمل implement للواجهة Comparable أو تعريف sort order مختلف عن الذي تقدمة الواجهة السابقة.
interface Comparator { int compare(Object o, Object v); boolean equals(Object object); }
الدالة compare تعيد:
- قيمة اكبر من الصفر, إذا كان o اكبر من v.
- اصغر من الصفر, إذا كان o اصغر من v.
- صفر, اذا كان o و v متطابقان.
قم بعمل المثال السابق باستخدام الواجهة Comparator.
الـــ Algorithmes :
مجموعة الـــ algorithmes التي تُمكن من التعامل مع الـــ collections موجودة في الكلاس Collections (انتبه جيداً, الكلاس Collections يختلف تماما عن الواجهة Collection). تم تعريف جميع تلك الدوال على أنها static.
نبدأ بالـــ algorithmes الخاصة بــ List :
الترتيب:
- sort(List list)l : ترتيب List
- sort(List list,Comparator comp)l : ترتيب List باستخدام comparator
الــ Merge :
shuffle(List liste)l : يخلط العناصر بطريقة عشوائية
البحث:
- binarySearch(List list, Object element)l : البحث عن عنصر باستخدام خوارزمية البحث الثنائي
- binarySearch(List list, Object element, Comparator comp)l : البحث عن عنصر باستخدام خوارزمية البحث الثنائي اعتمادا على comparator.
بعض العمليات الأخرى:
- reverse(List liste)l : قلب عناصر List
- fill (List liste, Object element)l : تقوم بعمل initialization لـ List باستخدام element
- copy(List dest, List src)l : نسخ src داخل dest
أيضا هناك خوارزميات أخرى يمكن تطبيقها على جميع الــ collections مثل :
- min (Collection)l : للبحث عن أصغر عنصر
- max (Collection)l : للبحث عن أكبر عنصر
ملاحظات:
- لاستخدام الدالة sort(List)l يجب أن تقوم كافة عناصر الـــ List بعمل implement للواجهة Comparable وإلا سيتم رفع استثناء من نوع ClassCastException.
- الكلاس Collections يحتوي على دوال خاصة تسمح بالحصول على نسخة multi-thread و Unmodifiable لمعظم الــ interfaces الموجودة مثل Collection, List, Map,Set, SortedMap, SortedSet :
- synchronizedXXX ( XXX)l : للحصول على نسخة multi-thread من الكائنات التي قامت بعمل impelement للواجهة XXX
- unmodifiableXXX (XXX)l : للحصول على نسخة Unmodifiable من الكائنات التي قامت بعمل impelement للواجهة XXX
مثال على خاصية الــ Unmodifiable :
public class TestUnmodifiable { public static void main(String args[]) { java.util.List list = new java.util.ArrayList(); list.add(new Integer(100)); list.add(new Integer(200)); list.add(new Integer(300)); java.util.List listun; listun = java.util.Collections.unmodifiableList(list); list.add(new Integer(400)); listun.add(new Integer(400)); // tentative illégale de modification de la liste l System.out.println(list.toString()); } }
محاولة تغيير Unmodifiable List تقتضي اصدار استثناء من نوع java.lang.UnsupportedOperationException.
مثال على خاصية الــ synchronization :
import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class Testsynchronized { public static void main(String[] args) { List list = new LinkedList(); list.add(new Integer(800)); list.add(new String("livres")); list.add("revue"); list.add("articles"); list.add(new Integer(122)); list.add(new Object()); list = Collections.synchronizedList(list); synchronized (list) { Iterator iter = list.iterator(); while (iter.hasNext()) { if (iter.next() instanceof Integer) { iter.remove(); } list.add(null); // illegal } } synchronized (list) { Iterator it = list.iterator(); while (it.hasNext()) { if (it.next() instanceof String) { it.remove(); } } } System.out.println(list.toString()); } }
استدعاء الدالة synchronizedXXX يُعيد كائن يدعم الــ synchronization لبعض العمليات مثل الاضافة و الحذف. أما بالنسبة لعملية المرور على العناصر باستخدام Iterator فيجب وضع الكود المخصص داخل synchronized bloc. يجب أيضا وضع استدعاء الدالة داخل الجزء المخصص.
محاولة اضافة عنصر جديد أثناء المرور على عناصر المجموعة, تقتضي اصدار استثناء من نوع java.util.ConcurrentModificationException.
مراجع:
Développons en Java – Jean Michel DOUDOUX
Notes de cours du mon Prof Joseph NDONG
Cours Java – Bernard Caylux
ماشاء الله موضوع كمية معلومات
لماذا النوع double لايستطيع أن يكون ضمن عملية comparable
لأنه يستخدم ال Generic وهي لا يمكن ان تستخدم primitive types سواء double, int. لذلك استخدم ال Wrapper classes مثلاً Double or Integer.