لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 10/15/22 في كل الموقع
-
1 نقطة
-
عملت سلايد في بل بوتسراب ومش عارف اخلي العنصر يجي في نص ومش عارف اخد سكرين شوت ازاي1 نقطة
-
السلام عليكم و رحمه الله و بركاته لو سمحتم عندي استفسار بسيط انا محمود من مصر في كليه الشريعه و القانون و حابب اني ادرس مجال الfront end develobr بس عندي استفسار هل لو اخدت الدوره في اكادميه حاسوب و اخدت الشهاده هيكون عندي فرصه اني اشتغل في الخليج مثلآ يعني هل الشهاده معتمده في الشركات و هل في طريقه مباشره ان اكادميه حاسوب ترشح طلابها للشركات في السوق العالمي حتى لو على سبيل التدريب بس اني اخد خبره و اقدر اشتغل بيها في شركه و انا عاوز اشتغل في شركه برا مصر ف هل هيحتاجو الشهاده الجامعيه1 نقطة
-
موقعي باللغة php ابغا شرط لكل مرة يتم فيها تسجيل الدخول يزيد عدد الزوار اللي داخلين الموقع1 نقطة
-
اضافة الى فكرة المدرب أحمد يمكنك تقييد عملية تزويد عدد المشاهدات بشرط أن يكون ذلك ضمن جلسة واحدة. بمعنى: ان قام المستخدم A بالدخول الى الصفحة B فإن عدد المشاهدات سيكون 1 ان قام المستخدم A بتحديث الصفحة B فان عدد المشاهدات سيكون 2 وان قام المستخدم A بزيارة صفحة أخرى في ذات الموقع فإن عدد المشاهدات سيتم تزويده هو الآخر، في حين أن العدد الفعلي هو زائر واحد. فالزيارات تحسب في الغالب ضمن الجلسة الواحدة. يمكنك مثلا انشاء متغير جلسة باسم isNew تخزن قيمة بعد أول زيارة للموقع. if(!isset($_SESSION['isNew'])){ $_SESSION['isNew'] = 'yes'; $visitors ++; } يتم تدمير الجلسة بعد انتهاءها.1 نقطة
-
ممكن تخليهم responsive بل بوتستراب وال وmedia query وبعدين انا اذاكر الكود textt.html textt.css1 نقطة
-
إذا كنت ترغب في إضافة عداد زوار PHP بسيط في موقع الويب الخاص بك ، فإليك حل بسيط يمكن أن يقودك إلى إضافة عداد زوار PHP بسيط لموقعك على الويب. سيعمل على موقع الويب بالكامل وسيحفظ بيانات العد في ملف نصي. والتي يمكن بسهولة إفراغها أو تغييرها. لاحظ أن هذا العداد سيعمل كملف نصي. هذه ليست قاعدة بيانات عداد. ولكن يمكنك أن تأخذ فكرة لحفظ عداد الزوار في عرض قاعدة البيانات. عداد الزوار في PHP قم بإنشاء ملف نصي يسمى visitors.txt قم بإنشاء ملف آخر يسمى index.php. الصق الكود أدناه فيه. كلما تم تحديث الملف بمقدار 1 زيادة في ملف visitors.txt. $visitors = file_get_contents('visitors.txt'); $visitors = $visitors+1; file_put_contents('visitors.txt',$visitors); سيحصل السطر الأول من الكود على المحتوى من الملف. تم إنشاء File_get_contents في وظيفة PHP. سوف يقوم السطر الثاني بعمل زيادة فيه. زائر + 1 يعني آخر زيارة و 1 زائد فيها. سيضع السطر الثالث قيمة الزيادة هذه مرة أخرى في ملف الزوار. الوسائل ستكتب في الملف.1 نقطة
-
نحن متوافرون كي نساعدك للوصول إلى المرحلة التي تقوم أنت بكتابة الكود للتنسيق الصحيح على جميع الشاشات، فإذا فعلنا ذلك عنك لم تحصّل أنت النتيجة المرجوّة، فيمكنك أن تطالع كيفية جعل الموقع متناسق مع جميع الشاشات باستخدام التقنيات التي تريدها من خلال المراجع التي سأرفقها، حاول أن تتعلم الخاصة ولماذا تستعمل وكيف تستعمل ثم استفد من ذلك في مشروعك. وفي حال تعرضت لمشاكل أثناء قيامك بالتنسيق أرجو منك السؤال عنها هنا وسنساعدك بقدر المستطاع. المراجع استعلامات الوسائط (Media Queries) في CSS دروس أكاديمة حسوب خاصة ببوتستراب الموقع الرسمي للبوتستراب Bootstrap كما يمكنك الاطلاع على السؤال التالي:1 نقطة
-
افضل برامج خدمة العملاء باللغة العربية هو برنامج زيتون ويتميز بواجهة عربية بالكامل. الموقع الرسمي لزيتون1 نقطة
-
في هذا الفيديو نشرح NoSQL أو مايعرف بـ Not Only SQL، حيث سنشرح لماذا قد نفكر باستخدام قواعد بيانات NoSQL والفائدة منها، ونعرفك بأنواع قواعد بيانات NoSQL ولماذا قد نختارها عوضًا عن قواعد البيانات التقليدية كـ MySQL أو PostgreSQL. بعد تعرفك على أساسيات تقنية NoSQL، يمكنك البدء في تعلم NoSQL وأنظمة إدارة قواعد البيانات باحترافية من خلال دورة علوم الحاسوب المقدمة من أكاديمية حسوب.1 نقطة
-
أكثر الإثارة التي تصحب عادة الحديث حول HTML5 تدور عادة حول الواجهات البرمجية الجديدة ويتعلق الأمر بكل من Local Storage، Application Cache، WebWorkers، الرسم ثنائي الأبعاد وما شابه. لكن يجب علينا أن لا نتجاهل بأن HTML5 يحمل في طياته 30 عنصرا جديدا لتعليم mark up المُستندات والتطبيقات مما يرفع عدد العناصر المتوفرة بشكل كلي إلى أكثر من 100 عنصر. باستثناء بعض الأمثلة والعروض الجوفاء، فإن أغلب التطبيقات المبنية بشكل أساسي على Javascript أو حتى التطبيقات المُغالية في مبادئ Web 2.0 ستحتوي على نصوص تحتاج أن يتم تعليمها بشكل حساس. دعونا نلقي نظرة على بعض العناصر الجديدة لتضمن بأن مشاريعك القادمة ستكون دلالية Sementic مثلما ستكون تفاعلية interactive. ما ستلاحظه طيلة هذا المقال هو أنه تم تصميم دلالية HTML5 لتقوم بتمديد ما يستطيع HTML القيام به، مع إتاحة مستخدمي المتصفحات القديمة الوصول إلى المُحتوى . سنرى أيضا بأن التعليمات الدلالية Sementic Markup ليس أمرا "من الجيد القيام به" فحسب وإنما يُعتبر حجز الزاوية لما يتعلق الأمر بتطوير تطبيقات الويب لأنها الآلية التي تسمح بتعزيز قابلية الوصول (Accessibility)، القابلية للبحث (searchability)، التدويل (internationalization) والتشغيل البيني (interoperability). اللغات البشرية كالإنجليزية وبالرغم من مفرداتها التي تُعد بالملايين فإنه لا يُمكنها أن تُعبر عن جميع الأفكار دون أن يكون هناك لُبس فيها، وبالتالي فإنه مع 100 مُفردة التي يُمكننا استخدامها في HTML فإننا سنجد أنفسها في حالات لا نعرف فيها على وجه اليقين أي عُنصر يجب علينا استخدامه لأي جزء من المُحتوى، في هذه الحالة كل ما عليك القيام به هو اختيار واحد واستخدامه بشكل متناسق في كامل الموقع. بعض عناصر العرض لم يعد لها وجود بعض عناصر العرض مثل center، font أو big أصبحت مهجورة في وقتنا الحاضر، حيث أنه تم استبدال أدوارها بـ CSS، لكن ذلك لا يعني بأنه يجب عليك أن تُسارع لإعادة كتابة كل تلك الصفحات القديمة التي كانت تستخدمها لأنه وبالرغم من كونها مهجورة ستقوم المُتصفحات بعرضها بشكل صحيح، حيث أن HTML5 دائما ما تهدف إلى توفير حلول جديدة دون أن تخل بالحلول الحالية. ولنفس الأسباب تم التخلص من بعض خصائص العرض التي كانت متوفرة من قبل في بعض العناصر مثلما هو عليه الحال مع خاصية align في عنصري img وtable ، خاصية background في عنصر body وخاصية bgcolor في عنصر table. العنصر "الشرير" frame أيضا مُغيب في HTML5 حيث أنه كان يُسبب مشاكل في قابلية الاستخدام وقابلية الوصول، لكن لو كانت لديك حاجة ماسة إلى استخدامه فكل ما عليك القيام به هو استخدام DOCTYPE أقدم لتبقى صفحاتك صحيحة من حيث البنية. يُمكنك أن تجد قائمة كاملة بكل العناصر والخواص التي تم حذفها على موقع W3C. تمت إعادة تعريف بعض عناصر العرض لتصبح دلالية لم يتم التخلص من جميع عناصر العرض، خضع بعضها لبرنامج إعادة تأهيل شامل وخرجت منه بخواص دلالية جديدة. فعلى سبيل المثال لم يعد عنصر small يعني "استخدم حجم خط أصغر" رغم أنه سيتم عرضه على هذا النحو في ملفات الأنماط الخاصة بالمتصفحات، حيث أنه أصبح يُمثل تعليقات جانبية، مثل تعليقات Small Print: يتم استخدام Small print في كل من تصريحات إخلاء المسؤولية، المحاذير، القيود القانونية وحقوق الملكية الفكرية، كما تستخدم أيضا لنسب الأعمال إلى أصحابها أو فيما يتعلق بالتراخيص. بعض عمليات إعادة التعريف قد تبدو مبالغا فيها، فإن كان بإمكان تفهم إعادة تعريف عنصر <b>(أو عنصر <i>) والذي تم بشكل في غاية الدقة (والذي تشوبه بعض الغرابة)، إلا أن إعادة تعريف بعض العناصر كعنصر <u> قد تصيبك بالدوار: The u element [now] represents a span of text with an unarticulated, though explicitly rendered, non-textual annotation, such as labeling the text as being a proper name in Chinese text (a Chinese proper name mark), or labeling the text as being misspelt. يُمكن قراءة المزيد حول العناصر والخصائص التي تم تحديثها على موقع W3C. عناصر دلالية جديدة وجذابة كلنا سمعنا عن عنصري video و audio الشهيرين. عنصر canvas أيضا شهير خاصة بسبب سماحه بتصميم رسوم ثلاثية الأبعاد بالاستعانة بـ WebGL والتي تسمح لمصممي الألعاب بنقل ألعابهم إلى الويب. ومثل عناصر img فإن هذه العناصر الجديدة تقوم بتضمين مُحتوى من مصادر أخرى سواء كانت ملفات، بيانات أو JavaScript. لكن على خلاف img فإن هذه العناصر لديها وسوم فتح وإغلاق مما يسمح بتوفير نوع من التراجع Fallback، وعليه فإنه سيكون بالإمكان توفير محتوى بديل للمتصفحات التي لا تدعم canvas ، ويُمكن لصورة مثلا أن تلعب دور التراجع لعنصر canvas، كما يُمكن لملف Flash أن يلعب دور التراجع لعنصر video وهذه التقنية تُسمى بتقنية "الفيديو للجميع". عنصرا source و track هما عنصران فارغان (من دون أي وسم إغلاق) ويأتيان كذرية (children) لعنصري video أو audio. يتم إعطاء عنصر source ملفات بتراميز مُختلفة، ويستعمل كل عنصر منها ملفا مختلفا (WebM، MP4، Ogg Theora) وسيقوم المتصفح بتشغيل أول هذه الملفات التي يُحسن التعامل معها: <audio controls> <source src=bieber.ogg type=audio/ogg> <source src=bieber.mp3 type=audio/mp3> <!-- fallback content: --> Download <a href=bieber.ogg>Ogg</a> or <a href=bieber.mp3>MP3</a> formats. </audio> في هذا المثال، سيقوم كل من Opera، Firefox وChrome بتحميل وتشغيل ملف Ogg ، في حين سيفضل Internet Explorer و Safari ملف MP3. يُمكن لمتصفح Chrome أن يُشغل كلا من Ogg وMP3 لكن المتصفحات ستقوم بتشغيل أول ملف تُحسن التعامل معه حسب الترتيب الذي وردت عليه. التراجع الذي نوفره هنا والذي يظهر قبل وسم الإغلاق يُوفر رابطا لتحميل الملف وتشغيله على جهازك باستخدام قارئ الصوتيات المُفضل لديك، كما أنه لا يظهر شوى على المُتصفحات التي لا يُمكنها قراءة الوسائط المُتعددة بشكل مُباشر. أما فيما يخص الفيديوهات فإنه بإمكانك تضمين ملف Flash على Youtube كنوع من التراجع: <video controls> <source src=best-video-ever.webm type=video/webm> <source src=best-video-ever.mp4 type=video/mp4> <!-- fallback content: --> <iframe width="480" height="360" src="http://www.youtube.com/embed/xzMUyqmaqcw?rel=0" frameborder="0" allowfullscreen> </iframe> </video> بهذه الطريقة سيصبح بإمكان مستخدمي المُتصفحات القديمة مثل IE6-8 أن يُشاهدوا الفيديو على Youtube (إن كانت مُتصفحاتهم تدعم Flash)، وسيكون بمقدورهم الوصول إلى تلك الفيديو. في حين سيحصل مستخدمو المتصفحات الأحدث على النسخة المُضمنة في الموقع من الفيديو، وبالتالي سيحصل الجميع على المحتوى الذي قدموا من أجله إلى موقعك. يُعتبر عنصر track إضافة جديدة إلى عائلة HTML5 وهو مدعوم على كل من Opera، Chrome وIE. يقوم هذا العنصر بالربط إلى ملف نصي يحتوي نصا وتوقيتا، يتم استخدامها لعرض الترجمة الفورية للفيديو وهي خاصية مفيدة جدا ليس فقط للذين يُعانون من مشاكل في السمع، بل أيضا للذين لا يتقنون اللغة التي تُعرض فيها الفيديو. الدلالية والتدويل قد يبدو هذا الأمر أقل أهمية من الوسائط المُتعددة لكن الأمر ليس كذلك بحكم أن الشبكة العنكبوتية العالمية ليس مُجرد محتويات باللغة الإنجليزية، بل أنها تستعمل أيضا أنظمة كتابة مُختلفة عن نظام الكتابة في اللغة الانجليزية. فبعض اللغات كالعربية والعبرية هي لغات تكتب من اليمين إلى اليسار على عكس اللغات الأوروبية والتي تُكتب من اليسار إلى اليمين. قد لا يشكل هذا الأمر أي إشكال على الصفحات التي تحتوي نظام كتابة واحد، لكن الأمر مُختلف على الصفحات التي تحتوي نظامي الكتابة في آن واحد، حيث أنه يجب على المُتصفح أن يقرر أين يتوجب عليه أن وضع علامات التنقيط، أزرار القوائم وأرقامها وما إلى ذلك. عادة ما تُبلي المُتصفحات بلاء حسنا باستخدامها للخوارزميات ثنائية الاتجاه الخاصة بـ Unicode لكنها تخطئ في بعض الحالات، وهو ما يصعب من مهمة فهم المحتوى. يوفر HTML5 عنصر bdi والذي يسمح للكتاب أن يقوموا بتجاوز الخوارزمية ثنائية الاتجاه آنفة الذكر لجعل نصوصهم أكثر سهولة للفهم. لوصف أوفى للمشكل ولمعرفة الطريقة التي يقوم العنصر bdi بحلها ألق نظرة على هذا المقال HTML5’s New bdi Element لكاتبه Richard Ishida المسؤول عن التدويل لدى W3C. بعض اللغات لا تملك أبجديات تقليدية بالمرة، والتي تُعبر عن أفكار بدل الأصوات، في بعض الحالات سيحتاج الكاتب بهذه اللغات إلى إضافة تنبيهات لتسهيل قراءة الكلمات خاصة مع الكلمات الشاذة أو الأحرف الغريبة، ويتم ذلك عادة بتوفير نص موازٍ بخص صغير فوق الحرف المعني بالأمر. عادة ما كان يتم القيام بذلك في المطبوعات باستخدام خط صغير ذي حجم 5 نقاط يُسمى « ruby » ، ولهذا يُوفر HTML5 على 3 عناصر جديدة للقيام بذلك، ويتعلق الأمر بكل من ruby، rt وrp. للمزيد من المعلومات حول الأمر، أقرأ المقال التالي: The HTML5 ruby Element in Words of One Syllable or Less الدلالية الهيكلية أغلب المُطورين على علم بأن HTML5 يوفر العديد من العناصر التي تسمح بوصف أجزاء مُختلفة من صفحات الويب مثل header، footer، nav، section، article، aside وغيرها. تم إنشاء هذه العناصر لأن المُطورين أمثالنا رغبوا في وجودها. قد تتساءل كيف عرف مطورو HTML5 العناصر التي نرغب فيها؟ كل ما في الأمر أن Google قامت سنة 2005 بتحليل مليار صفحة لمعرفة ما هي المُعرفات id والأصناف class التي كان المُطورون يستخدمونها مع عناصر div في تلك الصفحات. كما قامت Opera MAMA سنة 2008 بتحليل 3 ملايين URL لمعرفة أكثر أسماء الأصناف انتشارا والمعرفات الأكثر شيوعا على الإنترنت. كشفت هذه التحليلات بأن المطورين رغبوا في تعليم بعض أجزاء صفحاتهم لكن لم يكن لديهم خيار سوى استخدام عنصر div والذي أضافوا إليها أصنافا أو مُعرفات. كما سبق ذكره فإننا لن نتعمق في جميع هذه العناصر في هذا المقال، لكن يُمكنكم أن تجدوا تفاصيل كثيرة حولها على موقع HTML5Doctor. تم تصميم هذه العناصر الدلالية الجديدة لتوفر مبدأ التراجع الرشيق، فعلى سبيل المثال انظروا كيف يتم وصف عنصر figure في مواصفات HTML5: لم يأت هذا العنصر بأية أفكار جديدة حيث سبق لـ HTML3 أن اقترح عنصر fig لكنه لم يتم قبوله في مواصفات HTML3.2 والذي كان يُستعمل على النحو التالي: <FIG SRC="nicodamus.jpeg"> <CAPTION>Ground dweller: <I>Nicodamus bicolor</I> builds silk snares</CAPTION> <P>A small hairy spider. <CREDIT>J. A. L. Cooke/OSF</CREDIT></P> </FIG> هناك مُشكل كبير مع هذه الطريقة، حيث أنه وعلى المُتصفحات التي لا تدعم fig (ولا يوجد أي متصفح يدعمها) لن يتم عرض الصورة لأن المتصفحات ستقوم بتجاهل عنصر fig بشكل كامل، بل كل ما سيتم عرضه هو مُحتوى عنصر credit لأنه عبارة عن نص، وبالتالي ستحصل على وصف دون الصورة في المتصفحات القديمة. لكن مع HTML5 الوضع مُختلف، حيث أننا سنقوم بكتابة نفس المثال على النحو التالي: <figure> <img src="nicodamus.jpeg"> <figcaption> <p>Ground dweller: <i>Nicodamus bicolor</i> builds silk snares.</p> <p>A small hairy spider. <small>J. A. L. Cooke/OSF</small></p> </figcaption> </figure> على خلاف عنصر HTML3 الذي لم يتم قبوله فإن الطريقة التي نكتب بها المثال باستخدام HTML5 متوافقة مع الإصدارات القديمة من المُتصفحات، حيث أن المتصفحات التي لا "تعرف" عنصر figure ستقوم بعرض الصورة والنص الموجود في العنصر figcaption (مثلما كان سيتم عرض مُحتوى عنصر credit). لاحظوا بأننا نستخدم في هذا المثال عنصر small الذي تمت إعادة تعريفه بدل استحداث عنصر credit جديد. تذكروا دائما أنه يتم استعمال عنصر small لنسب الأعمال إلى أصحابها. يُوفر HTML5 عنصر figcaption جديدا. بداية كانت هناك رغبة لإعادة استخدام عنصر caption مثلما تم اقتراحه في HTML3، لكن كانت هناك مشاكل "إرثية" (Legacy problems) بحكم أن caption كانت تُستخدم سابقا كأحد ذرية عنصر table فقط. أحد مبادئ التصميم التي بُني عليها HTML5 هو أنه يجب على كل خاصية جديدة أن توفر تراجعا رشيقا، وإن لم يكن بإمكان العنصر القيام بذلك فإنه يجب عليه أن يسمح بتوفير محتوى تراجعي Fallback content، حيث أنه يتم تفضيل استخدام عناصر موجودة بدل استحداث عناصر جديدة، لكنه بحكم أن HTML5 لغة برمجية عملية فإنه لما يكون استحداث عنصر جديد أمرا ضروريا فإنها تقوم بذلك. دلالية تفاعلية لا تؤثر العناصر الهيكلية لـ HTML5 على مظهر الصفحات في المتصفحات كثيرا، إلا أن التطبيقات التي تُبنى على المتصفحات (خاصة تطبيقات قراءة الشاشات) قد شرعت في استخدامها (للمزيد حول الأمر: " HTML5, ARIA Roles, and Screen Readers in March 2011" و " JAWS, IE and Headings in HTML5.") بعض العناصر الأخرى لديها تأثير على شكل الصفحات، فعلى سبيل المثال العنصر details عبارة عن عنصر تفاعلي والذي يلعب دور نافذة صغيرة يحصل المستخدم لدى النقر عليها على بيانات أو خواص إضافية. ستقوم أغلب المُتصفحات إظهار هذا العنصر كصندوق قابل للتوسع، بعبارة أخرى لما يقوم المُستخدم بالنقر على أيقونة يقوم المُتصفح بتوليدها (كمثلث أو كسهم تحميل) أو حتى على كلمة "تفاصيل" (والتي سيكون بإمكان المُطور استبدالها في العنصر summary الذي سيكون ابنا مُباشرا للعنصر details)، وسيتم فتح الصندوق بشكل منسدل مظهرا التفاصيل التي يحتويها. من المُمكن أن يكون المحتوى عبارة عن وصف كامل للصورة أو للرسم البياني، وصفا لجدول مركب، خصائص للتحكم في نموذج بحث، أو أية معلومات أخرى. تُعتبر هذه الخاصية إحدى الخصائص الضرورية للويب الحديث، وبفضل HTML5 أصبح بالإمكان القيام بذلك من دون الحاجة إلى أي JavaScript. أغلبنا سبق لك أن شاهد مُختلف الخواص الدلالية الجديدة لنماذج HTML5 (تعرف على العناصر والخصائص الجديدة لنماذج HTML5)، والتي أغلبها عبارة عن خصائص للعنصر input والتي توفر تراجعا رشيقا إلى <input type=text> في المتصفحات الأقدم. من بين العناصر الجديدة نجد أيضا كلا من datalist، output، progress و meter. هل نملك العناصر الدلالية الأنسب؟ كما رأينا فإننا نملك عناصر دلالية جديدة عديدة، لكن السؤال الذي يجب أن نطرحه هو: هل فعلا نملك العناصر الدلالية الأنسب لاستعمالاتنا؟ مثلما رأينا فإن دراسة Google التي بُني عليها الأمر تعود إلى عدة سنوات خلت (2005)، ومن المُحتمل أن تكون الدلالية التي نعمل عليها متأخرة بعدة سنوات عن احتياجاتنا الحقيقية، كما لاحظنا فإن هذه العناصر تدور كلها هو مفهوم المُستند ومحتوياته document-centric وليست مبنية حول التطبيقات application-centric. هل نحتاج إلى عناصر دلالية تدول حول التطبيقات مثل login أو share، أو حتى Modal. ليس من السهل الإجابة على هذا السؤال، لكن كون HTML معيارا حيا living standard فإنه من المُمكن إضافة مثل هذه العناصر لاحقا لو تم اقتراح حالات استخدام قوية للـ Working Group. من دون شك سيرحب أغلب المُطورين بطرق جديدة لتضمين الصور والتي تتماشى مع مختلف الأجهزة التي يتم عليها. يُمكن مثلا أن نقتبس من عنصر video والذي يقوم بعرض الفيديو من المصدر الذي يتوافق مع المتصفح، يُمكن مثلا تخيل عنصر picture يكون على النحو التالي: <picture alt="angry pirate"> <source src=hires.png media="min-width:800px"> <source src=midres.png media="min-width:480px"> <source src=lores.png> <!-- fallback for browsers without support --> <img src=midres.png alt="angry pirate"> </picture> هذا العنصر سيعرض hire.png على الشاشات الكبيرة، midres.png على الشاشات التي يتراوح عرضها ما بين 480 و800 بكسل، و lores.png لباقي الشاشات. وهو ما قد يجيب أيضا على التساؤل الذي يطرحه عادة المصممون على أنفسهم: "هل يجب علي أن أطلب من جميع المتصفحات تحميل صورة عالية الجودة ومن ثم أقوم بتصغيرها للشاشات الصغيرة مما يعني بأنني سأقوم بتحميل بيانات زائدة، أم أقوم بتحميل صورة منخفضة الجودة ومن ثم أقوم بتكبيرها على الشاشات الكبيرة وبالتالي فإنني سأضحي بجودة الصور". وقياسا على ما سبق وأن رأيناه من عناصر دلالية شائعة فإنه يُمكن أيضا أن نوفر تراجعا رشيقا، والذي سيكون في هذه الحالة عنصرا من نوع img وبالتالي سيحصل الجميع على المحتوى الأصح. لا يزال إرسال الصورة بالحجم الأنسب أحد أعقد المشاكل فيما يتعلق بالتصاميم العبارة للأجهزة cross-device design وللتصاميم المتجاوبة responsive design. قد نجد حلا لها مع HTML6، لكن إلى غاية ذلك الحين تبقى أفضل الحلول -والتي من ضمنها حلا Adaptive Images و Responsive Images- تحتاج إلى JavaScript وإلى بعض التعديلات على ملف htaccess من جانب الخادوم. لكن أسوأ الحلول الحالية تتطلب تقنيات عفا عنها الزمن، كـ" اشتمام المتصفحات" Browser-sniffing، والذي تم تغيير اسمه ليصبح "اكتشاف الأجهزة" device detection رغم أنها تستعمل نفس التقنيات القديمة المتعلقة بالتحقق من user-agent والتي هي عبارة عن تقنية هشة جدا، لا تصلح مع التقنيات المستقبلية، كما أنها غير قابلة للتحجيم Scalable، دون أن ننسى بأنها تُذكرنا بالأيام التي كنا نشاهد فيها "لأفضل عرض استخدم متصفح netscape بدقة 800x600" على صفحات المواقع. متى، أين ومن؟ تحتاج الكثير من البيانات إلى المعلومات التالية: متى، أين ومن. يُوفر HTML5 عنصرtime (والذي أسال الكثير من الحبر) والذي يسمح لك بإرفاق التواريخ التي يُمكن لنا أن نقرها من دون صعوبة human-readable بتواريخ على هيأة قابلة للتحليل بشكل آلي machine-readable. لا يهم النص الذي تضعه ما بين وسمين الفتح والإغلاق، لأن ذلك النص هو الذي سيظهر للقراء، وبالتالي فإنه يُمكن استخدام أي من الصيغتين التاليتين: <time datetime="1982-07-18">The day the woman I love was born</time> <time datetime="1982-07-18">Priyanka Chopra’s birthday</time> أيا كانت الصيغة التي تختارها فإنه يمكن للآلة قراءة التاريخ بشكل صحيح مادام تم توفير التاريخ على شكل YYYY-MM-DD. إن أردت إضافة الوقت فإنه يمكنك ذلك لكن يجب عليك أن تفصله عن التاريخ بـ T وأن تستخدم وقتا من 24 ساعة (وليس من 12 ساعة) وتختمه بـ Z إضافة إلى أي اختلاف في الحزمة الزمنية. وبالتالي 2011-11-13T20:00Z تُمثل الساعة 8 مساء من يوم 13 نوفمبر سنة 2011 بالتوقيت العالمي UTC، في حين 2011-11-13T23:26.083Z-05.00 تُمثل الساعد 11 ليلا و26 دقيقة و83 جزء من الألف من الثانية في الحزمة الساعية التي تتواجد 5 ساعات قبل التوقيت العالمي. يُمكن مثلا لمتصفح في سيريلانكا أن يقوم بتحويل هذه المعلومة بشكل آلي إلى توقيت حسب التوقيت البوذي، كما أن مُحركات البحث قادرة على استخدام timestamps لتحديد مدى طزاجة نتائج البحث. رغم الانتشار الكبير لخدمات تحديد الأماكن الجغرافية geolocation إلا أننا لا نملك أي عنصر لتمثيلها والذي يقبل 3 خصائص: خطوط الطول، دوائر العرض، إضافة إلى الارتفاع (بشكل اختياري). وسيكون رائعا لو كان بإمكاننا كتابة شيء مثل التالي: <location lat=51.502064 long=-0.131981>London SW1A 4WW</location> وسيكون حينها بإمكان المتصفحات أن تقترح عليك خريطة فيما جملة من التوجيهات إلى المكان الذي تود أن تصل إليه اعتمادا على إحداثياتك الجغرافية الحالية. احتوى HTML3 على عنصر person تم استخدامه لأسماء الأفراد وذلك للتمكن من استخراجها بشكل آلي من طرف تطبيقات الأرشفة، لكنه لم يتم استخدامها. في HTML4 كان بالإمكان استخدام العنصر cite لتغليف أسماء الأشخاص، لكنه تم التخلص منه في HTML5 بشكل مثير للجدل، وبالتالي فإنه مع HTML5 لا نملك أية وسيلة لتحديد أسماء الأفراد بشكل واضح لا لُبس فيه. في المقابل فإن أسماء الأفراد يُعتبر مشكلا في غاية التعقيد، فإن كان للأوقات والتواريخ صيغ قياسية للتعبير عنها (YYYY-MM-DD للأولى و HH :MM :SS,mmm للثانية) وإن كان يتم تمثيل الأماكن الجغرافية دائما بخطوط الطول ودوائر العرض، فإن الأسماء أصعب للتحليل وللتقسيم إلى وحدات صغيرة يسهل التعامل معها، فعلى سبيل المثال هناك أسماء العائلات الروسية، الأسماء الإندونيسية أحادية الكلمات، الأسماء التي تحتوي على عدة أسماء للعائلة، دون أن ننسى الألقاب التايلاندية. يُمكنكم الاطلاع على مدى تعقيد الأمر بقراءة هذا المقال: Personal Names Around the World. يملك عنصر date الجديد -والذي استبدل عنصر time- خاصية تحدد قيمة الوقت الذي يُمكن قراءته بشكل آلي (machine-readable information)، لكنه لا يتطلب أو يتضمن صيغة خاصة للقيام بذلك، وبالتالي فإنه لا يُمكن لمحركات البحث مثلا أن تعلم إن كان 1936-10-19 تاريخا، جزءا من رقم، أو حتى رمزا بريديا. Microdata HTML5 تماما مثل سابقه HTML4 عبارة عن معيار قابل للتمديد extensible (لكنه ليس قابلا للتمديد على طريقة XML التي يذمها الـ Working Group). بإمكانك أن تستخدم الـ microformat التي سبق وأن تمت تجربتها واختبارها والتي تستعمل أصناف HTML (HTML classes) أو مواصفات RDFa الكاملة والتي لا يُمكنها أن تتجاوز اختبارات التحقق validation في كل من HTML4 أو HTML5. يتم اعتبار RDFa صعبة المراس، حيث أن Google أجرت بحثا بين بأنه يتم ارتكاب 30% أكثر من الأخطاء لدى استخدام RDFa مقارنة بغيرها من الصيغ). أما microdata فعبارة عن آلية لإضافة طبقة دلالية إضافية باستخدام مجموعة من الوسوم التي تم التفاهم عليها مُسبقا. يُمكنكم إيجاد المزيد حول الأمر على موقع HTML5doctor. مثلما هو عليه الحال مع microformat و RDFa، فإن الوسوم الدلالية التي نضيفها لن يكون لديها أي معنى ما لم يكن لدينا "وثيقة" تدلنا على المعنى الذي يحمله كل وسم. بعبارة أخرى هذه البيانات تحتاج إلى أن تربط إلى قاموس مفردات تُخبر التطبيقات الزاحفة crawler بأي طريقة يجب عليه أن يفسر البيانات التي يجدها. ولمن أراد استخدام microdata هناك موقع schema.org والذي يجمع مخططات/وسوم HTML يُمكن للمطورين استخدامها بطريقة تتعرف عليها كبريات مُحركات البحث. هل للويب الدلالي أي فائدة؟ بما أنه توليد الكثير من الوسوم أصبح يتم بشكل آلي عبر JavaScript فإن هناك من يعتقد بأن الدلالية لم يعد لها فائدة، عادة ما نُشاهد منتجات يتم التسويق لها على أساس أنها مُنتجات HTML5 لكنها بكل بساطة عبارة عن تطبيقات تقوم بجعل مجموعة من عناصر div "تطير" من مكان إلى آخر من الصفحة باستخدام JavaScript بنفس الطريقة المُتبعة مع DHTML منذ عشرية. بل أصبح هناك العديد من المُطورين الذين يطلقون تطبيقات لا تحتوي على أية وسوم، وهناك بعض أطر العمل التي تنتج هياكل HTML تحتوي على وسم body فارغ ومن ثم تقوم بحقنه بوسوم HTML باستخدام JavaScript. إن كنت ممكن يقومون بالقيام بذلك فأنت أقرب ما تكون من Flash منك إلى الويب. ومثلما أن هناك من يعتقد بأن قضاء 47 دقيقة (والتي قد يبدو بأنها مُدة طويلة) لبناء تصميم CSS هي مدة طويلة جدا ويتجهون مباشرة إلى استخدام الجداول في صفحاتهم، فإن هناك أيضا من يعتقد بأن قضاء بعض الوقت في التفكير في أي عنصر يجب استخدامه هو أيضا مضيعة وقت. هناك حجة أفضل والتي تقول بأنه لا يوجد أية تطبيقات تهتم أو تستهلك المحتويات الدلالية، فلماذا الاهتمام بها؟ رغم أن هذه الحجة ليس صحيحة لأن العمل جار على ربط تقنيات قابلية الوصول accessibility مع الدلالية الحديثة. لكن حتى لو كانت هذه الحجة صحيحة فإنها تتجاهل بأن المشكل الحالي هو مشكل مشابه لمشكل من أتى أولى، الدجاجة أم البيضة؟ لأنها تفترض بأنه لن تظهر أية محركات بحث جديدة يكون بمقدورها استخدام العناصر الجديدة، أو أن المتصفحات لن تطلق أية إصدارات جديدة تقوم باستغلال الدلالية فيها، أو أن المطورين لن يكتبوا أية إضافات للمتصفحات للقيام بذلك. باختصار هذه الحجة تفترض بأن دورة تطور الويب قد اكتملت. للويب الدلالي فائدة، لأن الدلالية تحمل معنى وبمجرد أن يتم اعتماد ذلك فإنه يُمكن للآلات أن تقوم باستغلال تلك البيانات من دون الحاجة إلى كتابة أو تطوير خوارزميات لتخمن معناها. سيكون بإمكان إضافة للمتصفح أن تسمح للمستخدم بالانتقال مباشرة إلى nav بنقرة زر واحدة لأنها ستقوم بالبحث عن nav مباشرة بدل البحث في كامل عناصر div وتخمن ما إذا كان المعرف id أو الصنف class الذي تحمله يوحي بأنها عبارة عن nav، هذا إن افترضنا أن المطور سيسمي هذا العنصر بأسماء لها معنى كـ nav، navigation، sidebar أو menu. كما أنه على موقع مطعم مثلا فإن div يحمل المعرف menu قد يحتوي على قائمة المأكولات المتوفرة بدل أمر آخر، وهو ما يذكرنا من جديد في مقدار الغموض الذي يلتبس لغاتنا الحية. وسيصبح مثلا بإمكان تطبيق زاحف crawler أن يجمع المقالات حسب ترتيبها الزمي مثلا. باختصار هناك الكثير من الحالات التي يُمكن التفكير فيها بناء على ذلك. تم بناء الويب اعتمادا على تقنيات بسيطة تم دمجها مع بعض لتعطينا نتائج مُذهلة وهي النتائج التي تجاوزت كل نوايا وتوقعات مطوريها الأوائل، وسيواصل الويب إبهارنا على هذا النحو. الأمر الذي جعل الويب مذهلا بهذه الطريقة والأمر الذي جعله مرنا وفي غاية القوة هو توفر المحتوى على شكل صيغ مفتوحة وبإمكاننا تحليله ودمجه بطرق مذهلة. يمكن لهذا أن يحدث لو تم وسم المحتوى من أجل إعطاء معنى له، ولو كان للغة وسوم يُمكن استخدامها كجزء من مفرداتنا. ما يقوم به HTML5 هو توسيع لدائرة المفردات التي نستعملها، بطبيعة الحال سنحتاج إلى مفردات إضافية والتي ستأتي مع HTML6. إذا كنت تعتقد بأن الويب عبارة عن نظام وجب عليه أن يعمل بنفس الطريقة على مختلف المتصفحات، وعلى مختلف أنظمة التشغيل وعلى مختلفة الأجهزة وباختلاف اللغات، وإن كنت تظن بأن شيفرته يجب أن تكون متوفرة للقراءة، وبأننا يجب أن يكون بمقدورنا القيام بمختلف العمليات عليه (القدرة على التغيير، والدمج، والقابلية للوصول، والقابلية للأرشفة ولإعادة الاستخدام) فإنه يجب علينا أن نضمن بأننا نستخدم الأدوات الدلالية التي في حوزتنا بشكل صحيح، وهو ما يضمن بأننا سنستفيد جميعا من الأمر. ترجمة –وبتصرف- للمقال HTML5 Semantics لصاحبه Bruce Lawson. اقرأ أيضًا دليل تعلم لغة HTML دليل تعلم البرمجة1 نقطة
-
اطلَّعنا بالمقال السابق على الخواص العامة لعناصر التجميعات بلغة جافا، وحان الآن الوقت لنفحص بعضًا من تلك الأصناف، ونتعرَّف على طريقة استخدامها، حيث يُمكِننا تقسيم تلك الأصناف في العموم إلى مجموعتين رئيسيتين، هما القوائم lists والأطقم sets؛ حيث تتكوَّن أي قائمةٍ من متتاليةٍ من العناصر المُرتَّبة خطيًا، بمعنى أنها مُرتَبة وفقًا لترتيب معين لا يُشترَط له أن يَكون ترتيبًا تصاعديًا؛ أما الطقم set فهو تجميعةٌ لا تحتوي أي عناصرٍ مُكرَّرة، وربما تكون العناصر مُرتّبةً بترتيبٍ مُحدَّد أو لا. سنناقش سريًعا نوعًا آخرًا من التجميعات إضافةً الى النوعين السابقين، يُعرَف باسم أرتال الأولوية priority queue؛ وهي بنيةٌ بيانيةٌ data structures مثل الأرتال ولكن عناصرها ذات أولوية. أصناف تمثيل القوائم تعرّفنا في مقال مفهوم المصفوفات الديناميكية (ArrayLists) في جافا ومقال بنى البيانات المترابطة Linked Data Structures على طريقتين لتمثيل القوائم، هما المصفوفات الديناميكية dynamic array والقوائم المترابطة linked list. تُوفِّر جافا الصنفين java.util.ArrayList و java.util.LinkedList ضمن إطار عمل جافا للتجميعات Java Collection Framework لتمثيلهما بصيغةٍ مُعمَّمة generic، حيث يُنفِّذ كلاهما الواجهة List<T>، وبالتالي الواجهة Collection<T> أيضًا. يُمثِل كائنٌ من النوع ArrayList<T> متتاليةً مُرتَّبةً من الكائنات المُنتمية إلى النوع T، والمُخزَّنة ضمن مصفوفةٍ يزداد حجمها تلقائيًا عند الضرورة،؛ بينما يُمثِل كائنٌ من النوع LinkedList<T> متتاليةً مُرتّبةً من الكائنات المُنتمية إلى النوع T، والمُخزَّنة -بخلاف المصفوفة- بعُقدٍ nodes تَربُطها مؤشرات pointers ببعضها بعضًا. يدعم الصنفان السابقان عمليات القوائم الأساسية المُعرَّفة بالواجهة List<T>، كما يُعرَّف أي نوع بيانات مُجرَّد abstract data type بعملياته وليس طريقة تمثيله representation. قد تتساءل: لماذا نحتاج إلى تعريف صنفين بدلًا من تعريف صنف قائمةٍ وحيد له نفس طريقة التمثيل؟ المشكلة هي أننا لن نتمكَّن من تمثيل القوائم بطريقةٍ واحدة، وتكون كفاءة جميع عمليات القوائم على ذلك التمثيل بنفس الدرجة؛ حيث تَكون كفاءة عمليات معينة أفضل دائمًا عند تطبيقها على القوائم المترابطة linked lists بالموازنة مع المصفوفات؛ بينما ستَكون كفاءة عمليات أخرى أفضل عند تطبيقها على المصفوفات. يعتمد أي تطبيق application عمومًا على مجموعةٍ معينة من عمليات القوائم أكثر من غيرها، ولذلك ينبغي اختيار التمثيل الذي تَبلغ فيه كفاءة تلك العمليات أقصى ما يُمكِن. يُعدّ الصنف LinkedList المُمثِّل للقوائم المترابطة مثلًا أكثر كفاءةً بالتطبيقات التي تعتمد بكثرة على إضافة العناصر إلى بداية القائمة ومنتصفها، أو حذفها من نفس تلك المواضع؛ حيث تتطلَّب نفس تلك العمليات عند تطبيقها على مصفوفة تحريك عددٍ كبيرٍ من العناصر مسافة موضعٍ واحدٍ إلى الأمام أو الخلف لإتاحة مساحةٍ للعنصر الجديد المطلوب إضافته، أو لملئ الفراغ الذي تسبَّب به حذف عنصر. إن زمن التشغيل المطلوب لإضافة عنصرٍ إلى بداية أو منتصف مصفوفة وفقًا لمصطلحات التحليل المقارب asymptotic analysis (انظر مقال تحليل الخوارزميات في جافا) يُساوِي Θ(n)، حيث تمثّل n عدد عناصر المصفوفة؛ أما بالنسبة للقوائم المترابطة، تقتصر عمليتي الإضافة والحذف على ضبط عددٍ قليل من المؤشرات، ويكون زمن التشغيل المطلوب لإضافة عقدةٍ node إلى أي موضعٍ ضمن القائمة أو حذفها من أي موضع مساوٍ إلى Θ(1)، أي تَستغرِق العملية مقدارً ثابتًا من الزمن بغض النظر عن عدد العناصر الموجود بالقائمة. من الجهة الأخرى، يُعدّ الصنف ArrayList المُمثِل للمصفوفات أكثر كفاءةً عندما تَكون عملية الجَلْب العشوائي random access للعناصر أمرًا ضروريًا؛ وهي ببساطة عملية قراءة قيمة العنصر الموجود بموضعٍ معين k ضمن القائمة، وتُستخدَم تلك العملية عند محاولة قراءة أو تعديل القيمة المُخزَّنة بموضعٍ معين ضمن القائمة. تُعدّ تلك العمليات أمرًا في غاية البساطة بالنسبة للمصفوفة، وتحديدًا بالنسبة لزمن التشغيل الذي يساويΘ(1)؛ أما بالنسبة للقوائم المترابطة linked list، فتَعنِي تلك العمليات البدء من بداية القائمة، ثم التحرك من عقدةٍ إلى أخرى على طول القائمة بعدد خطواتٍ يصل إلى k، ويَكون زمن التشغيل هو Θ(k). تتساوى كفاءة عملية الترتيب sorting وإضافة عنصرٍ إلى نهاية القائمة لكلا النوعين السابقين. تٌنفِّذ جميع أصناف القوائم توابع الواجهة Collection<T>، التي ناقشناها بالمقال السابق، مثل size() و isEmpty() و add(T) و remove(Object) و clear()؛ حيث يضيف التابع add(T) الكائن إلى نهاية القائمة؛ بينما يحاول التابع remove(Object) العثور أولًا على الكائن المطلوب حَذْفه باستخدام خوارزمية البحث الخطي linear search، التي لا تتميز بالكفاءة نهائيًا لأي نوعٍ من القوائم لأنها تتضمَّن المرور عبر جميع عناصر القائمة من بدايتها إلى نهايتها لحين العثور على الكائن. تحتوي الواجهة List<T> على عدة توابعٍ أخرى للوصول إلى عناصر القائمة عبر مواضعها العددية. لنفترض أن list كائنٌ من النوع List<T>، سيُصبِح لدينا التوابع التالية: list.get(index): يُعيد الكائن الموجود بالموضع index ضمن القائمة، حيث أن index هو عددٌ صحيح. لاحِظ أن العناصر مُرقَّمةٌ على النحو التالي: 0، 1، 2، .. إلى list.size()-1، وبالتالي، لا بُدّ أن تَقَع القيمة المُمرَّرة مثل مُعامِلٍ ضمن ذلك النطاق، وإلا سيَحدُث اعتراضٌ من النوع IndexOutOfBoundsException. list.set(index,obj): يَستبدِل الكائن obj بالكائن الموجود حاليًا بالموضع index ضمن القائمة، وبالتالي لا يُغيِّر التابع عدد عناصر القائمة، كما أنه لا يُحرِّك أيًا من عناصرها الأخرى. يجب أن يكون الكائن obj من النوع T. list.add(index,obj): يُدخِل الكائن obj إلى الموضع index ضمن القائمة؛ أي يُزيِد التابع عدد عناصر القائمة بمقدار الواحد؛ كما أنه يُحرِّك جميع العناصر الواقعة بعد الموضع index مسافة موضعٍ واحدٍ إلى الأمام لإتاحة مساحةٍ للعنصر الجديد. يجب أن يَكون الكائن obj من النوع T، كما يجب أن تتراوح قيمة index بين 0 و list.size()، وإذا كان index يُساوي list.size()، فسيُضيِف التابع الكائن obj إلى نهاية القائمة. list.remove(index): يَحذِف الكائن الموجود بالموضع index، ثم يعيده قيمةً للتابع؛ حيث يُحرِّك التابع العناصر الواقعة بعد ذلك الموضع مسافة موضعٍ واحدٍ إلى الخلف لملء الفراغ الذي تسبَّب به حذف العنصر، ويَقِل بذلك عدد عناصر القائمة بمقدار الواحد. يجب أن تتراوح قيمة index بين 0 و list.size()-1. list.indexOf(obj): يُعيد قيمةً عدديةً من النوع int تُمثِّل موضع الكائن obj بالقائمة في حال وجوده بها؛ بينما يُعيد القيمة -1 إذا لم يَكُن موجودًا. يُمكِن للكائن obj أن يَكون من أي نوع وليس فقط T. إذا كان الكائن obj موجودًا أكثر من مرةٍ ضمن القائمة، فسيُعيد التابع موضع أول حدوثٍ له. لاحِظ أن التوابع المذكورة أعلاه مُعرَّفةٌ لكلا الصنفين ArrayList<T> و LinkedList<T> على الرغم من أن كفاءة بعضها، مثل get و set مقتصرةٌ على الصنف ArrayList. يُعرِّف الصنف LinkedList<T> عدة توابع إضافية أخرى غير مُعرَّفةٍ بالصنف ArrayList. إذا كان linkedlist كائنًا من النوع LinkedList<T>، فسنحصل على التوابع التالية: linkedlist.getFirst(): يُعيد قيمةً من النوع T تُمثِّل أول عنصرٍ ضمن القائمة دون إجراء أيّ تعديلٍ عليها. سيحدث اعتراضٌ exception من النوع NoSuchElementException، إذا كانت القائمة فارغةً، وينطبق ذلك على التوابع الثلاثة التالية أيضًا. linkedlist.getLast(): يُعيد قيمةً من النوع T تُمثِّل آخر عنصرٍ ضمن القائمة دون إجراء أيّ تعديلٍ عليها. linkedlist.removeFirst(): يحذف الصنف أول عنصرٍ ضمن القائمة، ويُعيده قيمةً للتابع. التابعان linkedlist.remove() و linkedlist.pop() مُعرَّفان أيضًا، ولهما نفس دلالة التابع removeFirst(). linkedlist.removeLast(): يَحذِف آخر عنصرٍ ضمن القائمة، ويُعيده قيمةً للتابع. linkedlist.addFirst(obj): يُضيف الكائن obj إلى بداية القائمة، والذي يجب أن يكون من النوع T. التابع linkedlist.push(obj) مُعرَّفٌ أيضًا، وله نفس الدلالة. linkedlist.addLast(obj): يُضيف الكائن obj إلى نهاية القائمة، والذي يجب أن يكون من النوع T. يعمل بصورة مشابهة تمامًا للتابع linkedlist.add(obj)؛ فهو بالنهاية مُعرَّفٌ فقط للتأكد من الحصول على أسماء توابعٍ مُتسقّة consistent. ستلاحِظ وجود بعض التكرار ضمن الصنف LinkedList، لتسهيل استخدامه كما لو كان مكدسًا stack، أو رتلًا queue (انظر مقال المكدس Stack والرتل Queue وأنواع البيانات المجردة ADT). يُمكِننا على سبيل المثال استخدام قائمةٍ مترابطة من النوع LinkedList مثل مكدسٍ باستخدام التوابع push() و pop()، أو مثل رتلٍ باستخدام التوابع add() و remove() لتنفيذ عمليتي الإدراج enqueue والسحب dequeue. إذا كان الكائن list قائمةً من النوع List<T>، فسيعيد التابع list.iterator() المُعرَّف بالواجهة Collection<T> مُكرّرًا iterator من النوع Iterator، والذي يُمكِننا استخدامه لاجتياز traverse القائمة من البداية حتى النهاية. يتوفَّر أيضًا نوعٌ آخر من مُكرِّرات القوائم ListIterator يتميز بخواصٍ إضافية. لاحِظ أن الواجهة ListIterator<T> مُوسعَّةٌ من الواجهة Iterator<T>، ويُعيد التابع list.listIterator() كائنًا من النوع ListIterator<T>. تتضمَّن الواجهة ListIterator توابع المُكرِّرات العادية، مثل hasNext() و next() و remove()، ولكنها تحتوي أيضًا على توابعٍ أخرى، مثل hasPrevious() و previous() و add(obj) و set(obj)، والتي تُساعد على التحرُّك إلى الخلف ؛ وإضافة عنصرٍ بالموضع الحالي للمُكرِّر؛ واستبدال أحد عناصر القائمة على الترتيب. فكِّر بالمُكرِّرات كما لو كانت تُشير إلى موضعٍ بين عنصرين ضمن القائمة، أو إلى بداية القائمة، أو نهايتها لتتمكَّن من فِهم طريقة عمل التوابع السابقة. تُظهر الصورة التالية العناصر على هيئة مربعات، بحيث تُشير تلك الأسهم إلى المواضع المحتملة للمُكرِّر iterator: إذا كان iter مُكرِّرًا من النوع ListIterator<T>، فسيحركه التابع iter.next() مسافة موضعٍ واحدٍ إلى يمين القائمة، ويعيد العنصر الذي مرّ به المُكرِّر أثناء تحركه؛ ويُحرِّك التابع iter.previous() المُكرِّر مسافة موضعٍ واحد إلى يسار القائمة، ويعيد العنصر الذي مرّ به. يَحذِف التابع iter.remove() أحدث عنصرٍ مرّ به المُكرِّر أثناء تحركُّه أي بعد استدعاء التابع iter.next() أو التابع iter.previous(). يَعمَل التابع iter.set(obj) بنفس الطريقة، أي يستبدل obj بنفس العنصر الذي يفترض للتابع iter.remove() أن يَحذِفه عند استدعائه. يتوفَّر أيضًا التابع iter.add(obj) المسؤول عن إضافة الكائن obj من النوع T إلى الموضع الحالي للمُكرِّر، والذي من الممكن أن يكون في بداية القائمة، أو نهايتها، أو بين عنصرين موجودين مُسبَقًا ضمن القائمة. تُعدّ القوائم المُستخدَمة بالواجهة LinkedList<T> قوائمًا مترابطةً مزدوجة doubly linked lists، حيث تحتوي كل عقدةٍ node ضمن القائمة على مؤشرين pointers، يُشير أحدهما إلى العقدة التالية بالقائمة، بينما يشير الآخر إلى العقدة السابقة، ويُمكِّننا هذا من تنفيذ التابعين next() و previous() بأحسن كفاءةٍ ممكنة. كما يحتوي الصنف LinkedList<T> على مؤشر ذيل tail pointer للإشارة إلى آخر عقدةٍ ضمن القائمة، ويُمكِّننا ذلك من تنفيذ التابعين addLast() و getLast() بكفاءة. سنَدرِس الآن مثالًا عن كيفية استخدام مُكرِّرٍ من النوع ListIterator. لنفترض أننا نريد معالجة قائمةٍ من العناصر مع مراعاة الإبقاء عليها مُرتَّبةً ترتيبًا تصاعديًا. عند إضافة عنصرٍ إلى القائمة، سيَعثُر المُكرِّر من النوع ListIterator أولًا على الموضع الذي ينبغي إضافة العنصر إليه، ثم سيَضعُه به. يبدأ المُكرِّر ببساطةٍ من بداية القائمة، ثم يتحرَّك إلى الأمام بحيث يَمُر بجميع العناصر التي تقل قيمتها عن قيمة العنصر المطلوب إضافته، ويُضيِف التابع add() العنصر إلى القائمة عند هذه النقطة. إذا كان stringList مُتغيِّرًا من النوع List<String> مثلًا، وكان newItem السلسلة النصية التي نريد إضافتها إلى القائمة، وبِفَرض كانت السلاسل النصية الموجودة حاليًا ضمن القائمة مُرتَّبةً ترتيبًا تصاعديًا بالفعل، يُمكِننا إذًا استخدام الشيفرة التالية لوضع العنصر newItem بموضعه الصحيح ضمن القائمة بحيث نُحافِظ على ترتيبها: ListIterator<String> iter = stringList.listIterator(); // 1 while (iter.hasNext()) { String item = iter.next(); if (newItem.compareTo(item) <= 0) { // 2 iter.previous(); break; } } iter.add(newItem); حيث أن: [1] تعني حرِّك المُكرِّر بحيث يُشير إلى موضع القائمة الذي ينبغي إضافة newItem إليه؛ فإذا كان newItem أكبر من جميع عناصر القائمة، فستنتهي حلقة التكرار while عندما تُصبِح قيمة iter.hasNext() مُساويةً للقيمة false، أي عندما يَصِل المُكرِّر إلى نهاية القائمة. [2] تشير إلى يجب أن يأتي newItem قبل item. حرِّك المُكرِّر خطوةً للوراء، بحيث يُشير إلى موضع الإدخال الصحيح، وأنهي الحلقة. قد يكون stringList من النوع ArrayList<String>، أو النوع LinkedList<String>. لاحِظ أن كفاءة الخوارزمية المُستخدَمة لإدخال newItem إلى القائمة مُتساويةٌ لكليهما، كما أنها ستَعمَل مع أي أصنافٍ أخرى طالما كانت تُنفِّذ الواجهة List<String>. قد تجد أنه من الأسهل تصميم خوارزمية الإدراج باستخدام الفهرسة indexing على هيئة توابعٍ، مثل get(index) و add(index,obj)، ولكن ستكون كفائتها سيئةً للغاية بالنسبة للقوائم المترابطة LinkedList؛ لأنها لا تَعمَل بكفاءةٍ عند الجلب العشوائي random access. ملاحظة: ستَعمَل خوارزمية الإدراج insertion حتى لو كانت القائمة فارغة. الترتيب نظرًا لأن عملية ترتيب sorting القوائم من أكثر العمليات شيوعًا، كان من الضروري حقًا أن تُعرِّف الواجهة List تابعًا مسؤولًا عن تلك العملية، إلا أنه غير موجود؛ ربما لأن عملية ترتيب قوائم أنواعٍ معينة من الكائنات ليس لها معنى. بالرغم من ذلك، يتضمَّن الصنف java.util.Collections توابعًا ساكنة static methods للترتيب، كما يحتوي على توابعٍ ساكنةٍ أخرى للعمل مع التجميعات collections؛ وهي توابعٌ من النوع المُعمَّم generic، أي أنها تعمل مع تجميعات أنواعٍ مختلفة من الكائنات. لنفترض أن list قائمةً من النوع List<T>، يُمكِن للأمر التالي ترتيب القائمة تصاعديًا: Collections.sort(list); يجب أن تُنفِّذ عناصر القائمة الواجهة Comparable<T>. سيعمل التابع Collections.sort() على قوائم السلاسل النصية من النوع String، وكذلك لقوائم أي نوعٍ من الأصناف المُغلِّفة، مثل Integer و Double. يتوفَّر أيضًا تابع ترتيبٍ آخرٍ يَستقبِل معاملًا ثانيًا إضافيًا من النوع Comparator: Collections.sort(list,comparator); يُوازن المعامل الثاني comparator بين عناصر القائمة في تلك الحالة. كما ذكرنا بالمقال السابق، تُعرِّف كائنات الصنف Comparator التابع compare() الذي يُمكِننا من استخدِامه لموازنة كائنين. سنفحص مثالًا على استخدام الصنف Comparator في مقال قادم. يَعتمِد التابع Collections.sort() على خوارزمية الترتيب بالدمج merge sort بزمن تشغيل run time يساوي Θ(n*log(n)) لكُلٍّ من الحالة الأسوأ worst-case والحالة الوسطى average-case، حيث n هو حجم القائمة. على الرغم من أن زمن التشغيل لتلك الخوارزمية أبطأ قليلًا في المتوسط من خوارزمية الترتيب السريع QuickSort (انظر مقال التعاود recursion في جافا لمزيد من التفاصيل)، إلا أن زمن تشغليها في الحالة الأسوأ أفضل بكثير. تتميز خوارزمية الترتيب بالدمج MergeSort علاوةً على ذلك بخاصية الاستقرار stability، التي سنناقشها بمقال لاحق. يتضمَّن الصنف Collection تابعين آخرين مفيدين على الأقل لتعديل القوائم؛ حيث يُنظِم التابع الآتي: Collections.shuffle(list) عناصر القائمة بحيث تكون مُرتبةً ترتيبًا عشوائيًا؛ بينما يعكس التابع Collections.reverse(list) ترتيب عناصر القائمة، بحيث ينتقل آخر عنصرٍ في القائمة إلى مقدمتها، وثاني آخر عنصرٍ إلى الموضع الثاني بالقائمة، وهكذا. نظرًا لأن الصنف List يُوفِّر لنا بالفعل تابع ترتيب ذا كفاءة عالية، فلا حاجة لكتابته بنفسك. أصناف الأطقم TreeSet و HashSet يُعدّ الطقم set تجميعة كائنات، لا يتكرَّر فيها أي عنصرٍ أكثر من مرة. تُنفِّذ الأطقم جميع توابع الواجهة Collection<T> بطريقةٍ تَضمن عدم تكرار أي عنصرٍ مرتين؛ فإذا كان set كائن تجميعةٍ من النوع Set<T>، وكان يَحتوي على عنصرٍ obj، فلن يكون لاستدعاء التابع set.add(obj) أي تأثيرٍ على set. توفِّر جافا صنفين لتنفيذ الواجهة Set<T>، هما java.util.TreeSet و java.util.HashSet. بالإضافة إلى كون الصنف TreeSet من النوع Set، فإن عناصره تكون مُرتّبةً دائمًا ترتيبًا تصاعديًا، أي ستجتاز مُكرِّرات الأطقم من النوع TreeSet العناصر دائمًا بحسب ترتيبها التصاعدي. لا يُمكِن للأطقم من النوع TreeSet أن تحتوي على أية كائنات عشوائيًا؛ حيث لا بُدّ من معرفة الطريقة التي ينبغي على أساسها ترتيب تلك الكائنات؛ أي ينبغي لأي كائنٍ موجودٍ ضمن طقم من النوع TreeSet<T> أن يُنفِّذ الواجهة Comparable<T>، بحيث يَكون للاستدعاء obj1.compareTo(obj2) لأي كائنين obj1 و obj2 ضمن الطقم معنى. يُمكِننا بدلًا من ذلك تمرير كائنٍ من النوع Comparator<T> مثل معاملٍ للباني constructor عند إنشاء طقمٍ من النوع TreeSet، ويُستخدَم في تلك الحالة التابع compare() المُعرَّف ضمن Comparator لموازنة الكائنات المضافة إلى الطقم. لا تَستخدِم الأطقم من النوع TreeSet التابع equals() من أجل اختبار تساوي كائنين معينين، وإنما تَستخدِم التابع compareTo()، أو التابع compare()، وهذا قد يُحدِث مشكلة؛ لأن التابع compareTo() (كما ناقشنا بالمقال السابق) قد يُعامِل كائنين غير متساويين كما لو كانا كذلك لغرض الموازنة comparison، مما يَعنِي إمكانية وقوع أحدهما فقط ضمن طقمٍ من النوع TreeSet. لنفترض مثلًا أن لدينا طقمًا يحتوي على مجموعةٍ من عناوين البريد، وكان التابع compareTo() مُعرَّفٌ بحيث يوازن فقط الأرقام البريدية لتلك العناوين، وبالتالي يُمكِن للطقم أن يحتوي على عنوانٍ واحدٍ فقط لكل رقمٍ بريدي، وهو بالتأكيد أمرٌ غير منطقي. يجب إذًا الانتباه دومًا لدلالة الأطقم من النوع TreeSet، والتأكُّد من أن التابع compareTo() مُعرَّفٌ بطريقةٍ منطقية للكائنات المُتوقَّع إضافتها لهذا النوع من الأطقم، ويَنطبِق ذلك على السلاسل النصية من النوع String، والأعداد الصحيحة من النوع Integer، وغيرها من الأنواع الأخرى المبنية مُسبَقًا built-in؛ حيث يُعامِل التابع compareTo() الكائنات بتلك الأنواع على أنها متساوية إذا كانت فعلًا كذلك. تُخزَّن عناصر الأطقم من النوع TreeSet داخل ما يُشبِه أشجار الترتيب الثنائية binary sort tree (انظر مقال الأشجار الثنائية Binary Trees في جافا)، حيث تكون بنية البيانات data structure مُتزِّنةً؛ أي تكون جميع أوراق leaves الشجرة الثنائية على نفس البعد تقريبًا من جذر الشجرة root، مما يضمَن تنفيذ جميع العمليات الأساسية، مثل الإدْخال والحذف والبحث بكفاءة، وبزمن تشغيلٍ للحالة الأسوأ worst-case run time مساوٍ Θ(log(n))، حيث n هو عدد عناصر الطقم. كما ذكرنا مُسبقًا، تكون عناصر الأطقم من النوع TreeSet مُرتّبةً وغير مُكرَّرة، وهذا يجعلها مناسبةً لبعض التطبيقات. تَضمَّن تمرين 7.6 على سبيل المثال كتابة برنامجٍ يقرأ ملفًا ثم يَطبَع قائمة الكلمات الموجودة ضمن ذلك الملف بعد حذف جميع الكلمات المُكرَّرة، وبحيث تَكون مُرتّبةً أبجديًا. كنا قد اِستخدَمنا مصفوفةً من النوع ArrayList، وعليه كان من الضروري التأكُّد من كون عناصر المصفوفة مُرتّبةً وغير مُكرَّرة. يُمكننا في الواقع استخدام طقمٍ من النوع TreeSet لتخزين العناصر بدلًا من استخدام قائمة، وسيُبسِّط ذلك الحل كثيرًا؛ لأنه سيَحذِف العناصر المُكرَّرة تلقائيًا، كما سيجتاز مُكرِّر الطقم العناصر على نحوٍ مُرتّبٍ تلقائيًا. يُمكِننا كتابة الحل باستخدام الصنف TreeSet على النحو التالي: TreeSet<String> words = new TreeSet<String>(); // طالما ما يزال هناك بيانات أخرى بملف الدخل while there is more data in the input file: // أسنِد الكلمة التالية بالملف إلى word Let word = the next word from the file // حوِّل word إلى الحالة الصغيرة Convert word to lower case // أضِف word إذا لم تكن موجودةً بالفعل words.add(word) for ( String w : words ) // words في w من أجل كل سلسلة نصية Output w // تُطبَع الكلمات مُرتبة يُمكِنك أيضًا الاطلاع على الشيفرة الكاملة للبرنامج بالملف WordListWithTreeSet.java. لنفحص مثالًا آخرًا، بفرض أن coll تجميعةٌ من السلاسل النصية من النوع String، يُمكِننا استخدام طقمٍ من النوع TreeSet لترتيب عناصر التجميعة coll، ولحَذْف أي عناصر مُكرَّرة بكتابة الشيفرة التالية: TreeSet<String> set = new TreeSet<String>(); set.addAll(coll); تُضيِف التعليمة الثانية جميع عناصر التجميعة إلى طقم، وبما أنه من النوع Set، فسيتجاهل العناصر المُكرَّرة تلقائيًا، ونظرًا لكونه من النوع TreeSet تحديدًا، ستكون العناصر مُرتَّبة. إذا أردت تخزين بيانات طقمٍ معينٍ داخل بنية بيانات data structure مختلفة، يُمكِنك ببساطة نسخها من الطقم. تَنسَخ الشيفرة التالية عناصر طقمٍ إلى مصفوفةٍ من النوع ArrayList: TreeSet<String> set = new TreeSet<String>(); set.addAll(coll); ArrayList<String> list = new ArrayList<String>(); list.addAll(set); تَستقبل بناة constructors جميع الأصناف المُمثِلة للتجميعات ضمن لغة جافا تجميعةً من النوع Collection؛ وعند استدعاء إحداها، ستُضَاف جميع عناصر التجميعة المُمرَّرة إلى التجميعة الجديدة المُنشَئة. إذا كان coll من النوع Collection<String> مثلًا، يُنشِئ الاستدعاء new TreeSet<String>(coll) طقمًا من النوع TreeSet يحتوي على نفس العناصر الموجودة بالتجميعة coll بعد حذف أي عناصرٍ مُكرَّرة، كما أنها تكون مُرتَّبة. يُمكِننا بناءً على ذلك إعادة كتابة الأسطر الأربعة السابقة على النحو التالي: ArrayList<String> list = new ArrayList<>( new TreeSet<>(coll) ); تُنشِيء التعليمة السابقة قائمةً مُرتبةً من العناصر غير المُكرَّرة ضمن التجميعة coll. يُبيّن المثال السابق مدى فعالية البرمجة المُعمَّمة generic programming. لاحِظ أنه من غير الضروري كتابة معامل النوع String بالبانيين السابقين؛ لأن المُصرِّف compiler قادرٌ على استنتاجهما بالفعل. تُخزِّن الأطقم من النوع HashSet عناصرها ضمن بنيةٍ بيانية تُعرَف باسم جدول hash table، وسنتناول تلك البنية البيانية في المقال الموالي. تَعمَل عمليات البحث والإضافة والحذف على الجداول بكفاءة عالية، وأعلى حتى من الصنف TreeSet. بخلاف الصنف TreeSet، لا تُخزِّن الأطقم من النوع HashSet عناصرها وفقًا لأي ترتيبٍ مُحدَّد، وبالتالي لا تَكون مُضطّرةً لتنفيذ الواجهة Comparable؛ ولكن ينبغي في المقابل أن تُعرِّف شيفرة تعمية hash code مناسبة كما سنرى بالمقال التالي. يُحدِّد التابع equals() فيما إذا كان من الممكن عدّ كائنين بطقمٍ من النوع HashSet متساويين، حيث تَمرّ مُكرِّرات أطقم النوع HashSet عبر عناصرها مرورًا عشوائيًا، بل قد يتغيَّر ترتيب مرورها بالعناصر مع إضافة عنصرٍ جديد. اِستخدِم الصنف HashSet بدلًا من الصنف TreeSet إذا لم تَكْن العناصر قابلة للموازنة، أو إذا لم يَكْن ترتيبها مُهمًا، أو إذا كنت مهتمًا بكفاءة العمليات على العناصر أكثر من أي شيءٍ آخر. ملاحظة: يُطلق على عناصر الأطقم وفقًا لنظرية المجموعات set theory الحسابية أعضاء members أو عناصر elements. وتتضمَّن العمليات الهامة على تلك الأطقم ما يلي: إضافة عنصرٍ إلى مجموعة، وحذف عنصرٍ من مجموعة، وفحص فيما إذا كانت قيمةٌ ما عنصرًا ضمن مجموعة. إذا كان لدينا طقمين، يُمكِننا إجراء العمليات التالية عليهما: توحيد union طقمين، وتقاطع intersection بين طقمين، والفرق بين طقمين. تُوفِّر جافا تلك العمليات للأطقم من النوع Set، ولكن بأسماءٍ مختلفة. بفرض أن لدينا طقمين A و B، فإن: A.add(x): يُضيف العنصر x إلى الطقم A. A.remove(x): يحذف العنصر x من الطقم A. A.contains(x): يفحص إذا كانت x عنصرًا بالطقم A. A.addAll(B): يحسب اتحاد الطقمين A و B. A.retainAll(B): يحسب التقاطع بين الطقمين A و B. A.removeAll(B): يحسب الفرق بين الطقمين A - B. تختلف الأطقم بمفهومها الحسابي عن الأطقم بلغة جافا بالنقاط التالية: يجب أن تكون الأطقم نهائية finite، بينما تكون المجموعات الحسابية عادةً لا نهائية. قد تحتوي المجموعات الحسابية على عناصر عشوائية، بينما تكون الأطقم من نوعٍ محدد مثل Set<T>، ولا يُمكِنها أن تحتوي على أية عناصر غير مُنتمية للنوع T. تُعدِّل العملية A.addAll(B) قيمة A، بينما تَحسب عملية الاتحاد بين الطقمين A وB طقمًا جديدًا دون أن تُعدِّل من قيمة أيٍّ من الطقمين الأصليين. سنتعرض بالتمرين 10.2 لمثالٍ عن العمليات الحسابية على الأطقم. أرتال الأولوية يُعدّ رتل الأولوية priority queue نوعًا بيانيًا مجردًا abstract data type يُمثِّل تجميعة عناصر، حيث يُسنَد إلى كل عنصرٍ منها أولوية priority معينة، وهو ما يَسمَح بالموازنة بينها. تتضمَّن العمليات على أرتال الأولوية ما يلي: عملية add المسؤولة عن إضافة عنصرٍ إلى التجميعة. عملية remove المسؤولة عن حذف العنصر ذو الأولوية الأقل من التجميعة وإعادته قيمةً لعملية الحذف ذاتها. عملية الحذف remove بحيث تَحذف العنصر ذا الأولوية الأقل، ولكن من الممكن نظريًا حذف العنصر ذي الأولوية القصوى. يُمكنِنا تنفيذ رتل الأولوية باستخدام قائمةٍ مترابطة linked list لتخزين العناصر بحيث تكون مُرتبةً تصاعديًا وفقًا لترتيب أولوياتها. تَحذِف remove في تلك الحالة أول عنصرٍ ضمن القائمة وتُعيده؛ بينما يجب على عملية add إضافة العنصر الجديد بموضعه الصحيح ضمن القائمة، وهو ما يستغرق زمن تشغيل وسطي قدره Θ(n)، حيث n هي عدد عناصر القائمة. يُمكِننا أيضًا تنفيذ رتل الأولوية بطريقةٍ أكثر كفاءةً بحيث يكون زمن تشغيل عمليتي add و remove مُساويًا Θ(log(n))؛ وتعتمد تلك الطريقة على استخدام بنية بياناتٍ تُعرَف باسم الكومة heap، وهي مختلفةٌ عن قسم الكومة بالذاكرة الذي تُنشأ فيه الكائنات objects. يُنفِّذ الصنف PriorityQueue<T> ذو المعاملات غير مُحدَّدة النوع parameterized رتل أولوية للكائنات من النوع T، كما يُنفِّذ الواجهة Collection<T>. فإذا كان pq رتل أولويةٍ من النوع PriorityQueue، فسيحتوي على جميع التوابع methods المُعرَّفة ضمن تلك الواجهة interface. سنستعرِض فيما يلي أكثرها أهمية: pq.add(obj): يُضيف obj إلى رتل الأولوية. يجب أن يكون obj كائنًا من النوع T. pq.remove(): يَحذِف أقل العناصر أولوية، ويعيدها أي تكون القيمة المُعادة كائنٌ من النوع T، وإذا كان الرتل فارغًا، يَحدُث اعتراض exception. pq.isEmpty(): يَفْحَص إذا كان رتل الأولوية فارغًا. سنفحص الآن الطريقة التي تتحدَّد على أساسها أولوية العناصر ضمن رتل أولوية، وهي تشبه عملية الترتيب، ولهذا يجب أن نكون قادرين على موازنة أي عنصرين داخل الرتل. قد نواجه موقفًا من اثنين: إما أن تكون العناصر مُنفِّذة للواجهة Comparable، ويُستخدَم عندها التابع compareTo() المُعرَّف بتلك الواجهة لموازنة العناصر؛ أو أن نُمرِّر كائنًا من النوع Comparator مثل معاملٍ لباني الصنف PriorityQueue ويُستخدَم في تلك الحالة التابع compare المُعرَّف بالنوع Comparator للموازنة. يُمكِننا استخدام الأصناف المُنفِّذة للواجهة Comparable، مثل String و Integer و Date مع أرتال الأولوية. فعلى سبيل المثال، قد نَستخدِم رتل أولوية من السلاسل النصية PriorityQueue<String> لنُرتِّبها ترتيبًا أبجديًا على النحو التالي: سنُضيِف جميع السلاسل النصية إلى رتل الأولوية، ثم نَحذِفها واحدةً تلو الأخرى. وبما أن عناصر أرتال الأولوية تُحذَف بحسب أولويتها، فستَجِد أنها تُحذَف بحسب ترتيبها الأبجدي. كنا قد أوضحنا سابقًا استخدام طقمٍ من النوع TreeSet لترتيب تجميعةٍ من العناصر، وكذلك لحذف المُكرَّر منها، ويُمكِننا بالمثل استخدام الصنف PriorityQueue لترتيب عناصر تجميعة، ولكن بدون حذف أي عنصرٍ حتى المُكرَّر منها. إذا كانت coll مثلًا تجميعةً من النوع Collection<String>، فستطبع الشيفرة التالية جميع عناصرها بما في ذلك المُكرَّر منها: PriorityQueue<String> pq = new PriorityQueue<>(); pq.addAll( coll ); while ( ! pq.isEmpty() ) { System.out.println( pq.remove() ); } ملاحظة: لا يُمكِن اِستخدَام مُكرِّر iterator أو حلقة for-each لطباعة العناصر بالمثال السابق، لأنها لا تجتاز عناصر أرتال الأولوية priority queue وفقًا لترتيبها التصاعدي. يُنشِئ البرنامج التوضيحي WordListWithPriorityQueue.java قائمةً مُرتّبةً من الكلمات الموجودة بملفٍ معين دون أن يَحذِف أيّ كلماتٍ مُكرَّرة، حيث يُخزِّن البرنامج الكلمات برتل أولوية. يُمثِل هذا البرنامج تعديلًا بسيطًا على البرنامج الأصلي WordListWithTreeSet.java. تُستخدَم أرتال الأولوية في تطبيقاتٍ أخرى غير الترتيب، مثل تنظيم عملية تنفيذ الحاسوب لعدة وظائف jobs ذات أولوياتٍ مختلفة، وبحيث يكون ترتيب التنفيذ من الوظائف ذات الأقل أولوية فالأعلى. يُمكِننا بناءً على ذلك تخزين الوظائف برتل أولوية؛ وعندما يَحذِف الحاسوب وظيفةً من الرتل لينفِّذها، سيَحذِفها وفقًا للترتيب التصاعدي لأولويتها. ترجمة -بتصرّف- للقسم Section 2: Lists and Sets من فصل Chapter 10: Generic Programming and Collection Classes من كتاب Introduction to Programming Using Java. اقرأ أيضًا تحليل زمن تشغيل القوائم المنفذة باستخدام مصفوفة تحليل زمن تشغيل القوائم المنفذة باستخدام قائمة مترابطة مفهوم المصفوفات الديناميكية (ArrayLists) في جافا1 نقطة