عند إنشاء برامج بدرجات متفاوتة من التعقيد، يستخدم كل مطور العديد من أنواع البيانات، بما في ذلك المصفوفات. هذا الهيكل مناسب تمامًا لتخزين مجموعة من نوع واحد، ويوفر أداءً رائعًا، كما أنه مريح بشكل عام. العيب الكبير للمصفوفات هو أنها ثابتة: يجب تحديد حجمها مسبقًا. ومع ذلك، لا يعرف المبرمجون بعد كيفية التنبؤ بالمستقبل (ما لم يظهر الذكاء الاصطناعي، بالطبع، أنه سيعالج المعلومات بسرعة لا تصدق ويكون قادرًا على التنبؤ بأي أحداث). لهذا السبب، قمنا بإنشاء هيكل يمكنه تغيير حجمه أثناء تشغيل البرنامج. يطلق عليه مجموعة ديناميكية .
المصفوفات الديناميكية في دورة JavaRush
تمت تغطية هذا الموضوع بشكل واضح وواضح جدًا في المستوى 7 وجزئيًا في المستوى 8 من دورة JavaRush في مهمة Java Syntax. على مدار عدة محاضرات وما يصل إلى 18 مشكلة، يتم تناول القضايا الرئيسية وأنواع المصفوفات الديناميكية والفرق بينها، بما في ذلك الأداء. هذا الموضوع مهم للغاية، لأن المصفوفات الديناميكية تريح المطور من الاكتئاب والصداع وتوفر قدرًا لا يصدق من الوقت.
ما هي المصفوفة الديناميكية؟
المصفوفة الديناميكية هي مصفوفة يمكنها تغيير حجمها أثناء تنفيذ البرنامج. في Java، يتم لعب هذا الدور بشكل أساسي بواسطة فئتي ArrayList وLinkedList. على عكس المصفوفات، تحتوي ArrayList وLinkedList على أنواع بيانات مرجعية فقط، أي أنها يمكنها تخزين الكائنات فقط. لحسن الحظ، لدى Java آليات التشغيل التلقائي والتشغيل التلقائي التي تسمح لك بتخزين الأنواع البدائية في المصفوفات الديناميكية. مثل المصفوفة الثابتة، تكون المصفوفة الديناميكية متجانسة، أي يمكنها تخزين نوع بيانات واحد. ومع ذلك، بفضل آلية الميراث والاستخدام السليم للواجهات، من الممكن تخزين مجموعة كاملة من الفئات المختلفة الموروثة من فئة واحدة مشتركة في مصفوفة ديناميكية واحدة، ولكن المزيد عن ذلك أدناه. أي أن المصفوفة الثابتة تعمل على النحو التالي: وستعمل المصفوفة الديناميكية في Java على النحو التالي (استمرارًا للرسم التخطيطي من الخطوة الثالثة): تستخدم Java وظيفة أصلية خاصة لنسخ المصفوفة، لذا فإن مثل هذه "الحركة" ليست مفيدة جدًا غالي.لماذا نحتاج إلى مجموعة ديناميكية؟
يتم استخدام المصفوفة الديناميكية في Java لمعالجة مجموعات من البيانات المتجانسة التي يكون حجمها غير معروف في وقت كتابة البرنامج. على سبيل المثال، قد ترغب في تخزين بيانات كل عميل يستخدم التطبيق حاليًا. من المستحيل التنبؤ بعدد هؤلاء العملاء مسبقًا. بدون المصفوفات الديناميكية، يمكن حل هذه المشكلة بالخيارات التالية:- إنشاء مصفوفة كبيرة من المحتمل أن تغطي الحاجة بنسبة 100%؛
- قم بإنشاء مصفوفة ثابتة تعمل كمخزن مؤقت؛
- تطبيق الهياكل الديناميكية الأخرى، مثل المجموعات.
ما هي وظيفة المصفوفة الديناميكية في Java
في لغة Java، تعمل فئات ArrayList وLinkedList كمصفوفة ديناميكية. الأكثر استخدامًا هو ArrayList، لأنه يعمل كمصفوفة كلاسيكية، على عكس LinkedList، الذي يطبق مفهوم القائمة المرتبطة بشكل مضاعف. سنتحدث عن ذلك بعد قليل.ArrayList، LinkedList - المفاهيم وقواعد التشغيل
ArrayList عبارة عن مصفوفة كلاسيكية يمكن توسيعها أثناء تنفيذ البرنامج. يعتمد على مصفوفة عادية: حجمها عند إنشائها هو 10 عناصر. كلما زاد الحجم زادت السعة. القواعد التي يعمل بها ArrayList:- تمامًا مثل المصفوفة الثابتة، تتم فهرستها من 0؛
- الإدراج في النهاية والوصول عن طريق الفهرس سريعان جدًا - O(1);
- لإدراج عنصر في البداية أو المنتصف، ستحتاج إلى نسخ جميع العناصر خلية واحدة إلى اليمين، ثم لصق عنصر جديد في الموضع المطلوب؛
- يعتمد الوصول حسب القيمة على عدد العناصر - O(n);
- على عكس المصفوفة الكلاسيكية، يمكنها تخزين قيمة فارغة؛
Head
، يقوم بتخزين معلومات حول عدد العناصر، بالإضافة إلى رابط للعنصرين الأول والأخير: الآن الحقل size = 0
هو first
و last = null
. كل عنصر يضاف إلى هذه القائمة هو محتوى كائن داخلي منفصل. لنقم بإضافة عنصر Johnny
: الآن لدينا عقدة بقيمة "جوني". بالنسبة للعنصر الرئيسي، تشير الروابط إلى العنصر الأول والأخير إلى العقدة الجديدة. يحتوي هذا الكائن أيضًا على روابط للعناصر السابقة والتالية. الرابط إلى العنصر السابق سيكون دائمًا فارغًا، لأن هذا هو العنصر الأول، والرابط إلى العنصر التالي سيكون دائمًا فارغًا، لأنه غير موجود بعد. دعونا نصلح هذا: تمت إضافة عنصر جديد بقيمة "Watson"، والذي أصبح العنصر الثاني. يرجى ملاحظة أن العنصر الأول يحتوي على حقل next
يشير إلى العنصر التالي، والعنصر الجديد يحتوي على حقل previous
يشير إلى العنصر السابق. بالنسبة للعنصر الرئيسي، يشير الرابط إلى العنصر الأخير الآن إلى العقدة الجديدة. ويوضح الرسم البياني التالي كيفية إضافة عناصر إلى منتصف القائمة: تمت إضافة عنصر جديد "هاميش". ولإدراجه في منتصف القائمة، ما عليك سوى إعادة تعيين الروابط إلى العناصر، كما هو موضح في الشكل. تشرح هذه الرسوم التوضيحية عملية إنشاء قائمة مرتبطة بشكل مزدوج في المستوى الأعلى، دون الخوض في التفاصيل. لتلخيص قصة LinkedList، يمكننا استخلاص عدة قواعد لتشغيلها:
- تمامًا مثل المصفوفة، تتم فهرستها من 0؛
- الوصول إلى العنصر الأول والأخير لا يعتمد على عدد العناصر - O(1);
- الحصول على عنصر حسب الفهرس أو الإدراج أو الحذف من منتصف القائمة يعتمد على عدد العناصر - O(n);
- يمكنك استخدام آلية التكرار: ثم سيحدث الإدراج والحذف في وقت ثابت؛
- على عكس المصفوفة الكلاسيكية، يمكنها تخزين قيمة فارغة.
أمثلة على التعليمات البرمجية
دعونا نذهب من خلال بعض الأمثلة. تتضمن مقتطفات التعليمات البرمجية أمثلة لكل من ArrayList وLinkedList.خلق
// Создаем новый список
ArrayList<String> arrayList = new ArrayList<>();
// Создается новый список и указывается начальный размер внутреннего массива
ArrayList<String> arrayListLarge = new ArrayList<>(100000);
// Создаем новый LinkedList
LinkedList<String> linkedList = new LinkedList<>();
إضافة عنصر
// Новый элемент добавляется в конец
arrayList.add("Johhny");
// Новый элемент добавляется в указанную позицию (в данном случае — в начало)
arrayList.add(0, "Watson");
// Новый элемент добавляется в конец двусвязного списка
linkedList.add("Java");
// Новый элемент добавляется в нулевую позицию списка:
linkedList.addFirst("I think");
// Новый элемент добавляется в конец списка
linkedList.addLast("language");
// Новый элемент добавляется в указанную позицию
linkedList.add(2, "is a terrific");
// Получение размера списков
int arraySize = arrayList.size(); // 2
int linkedSize = linkedList.size(); // 4
للوهلة الأولى، تؤدي أساليب add()
AND addLast()
نفس الوظيفة، لكن الطريقة add()
جاءت إلى LinkedList من الواجهة List
، وجاءت الطريقة addLast
من الواجهة Deque
. يقوم LinkedList بتنفيذ هاتين الواجهتين. من الممارسات الجيدة في هذه الحالة استخدام الطريقة الأكثر ملاءمة للسياق. إذا تم استخدام LinkedList كقائمة انتظار، فمن الأفضل استخدام الملف addLast
. إذا تم استخدام LinkedList كقائمة، فسيكون من المناسب استخدام add()
.
إزالة عنصر
// Удаление element по индексу
arrayList.remove(0);
// Удаление element по значению
arrayList.remove("Johnny");
// Удаление первого element в списке
linkedList.removeFirst();
// Удаление первого element в списке, фактически вызов предыдущего метода
linkedList.remove();
// Удаление последнего element в списке
linkedList.removeLast();
// Удаление первого вхождения element в список
linkedList.removeFirstOccurrence("language");
// Удаление последнего вхождения element в список
linkedList.removeLastOccurrence("Java");
// Удаление по индексу
linkedList.remove(2);
إذا تم حذف كائن بواسطة الفهرس، تقوم الطريقة بإرجاع الكائن المحذوف. إذا تم حذف كائن حسب القيمة (أو تم حذف العنصر الأول أو الأخير من LinkedList)، فسترجع الطريقة صحيحًا إذا تم العثور على الكائن وحذفه، وإلا فسترجع خطأ .
الوصول إلى عنصر والبحث في القائمة
// Доступ к элементу по индексу
String arrayElement = arrayList.get(2);
// Поиск element по значению
int arrayIndex = arrayList.indexOf("Watson");
// Поиск последнего индекса вхождения element в список
int lastArrayIndex = arrayList.lastIndexOf("Watson");
// Доступ по индексу
String linkedElement = linkedList.get(3);
// Получение первого element
String firstLinkedElement = linkedList.getFirst();
// Получение последнего element
String lastLinkedElement = linkedList.getLast();
// Поиск element по значению
int linkedIndex = linkedList.indexOf("Java");
// Поиск последнего индекса вхождения element в список
int lastLinkedIndex = linkedList.lastIndexOf("Java");
المشي في حلقة
// Использование обычного цикла
for(int i = 0; i<arrayList.size(); i++) {
String value = arrayList.get(i);
System.out.println(value);
}
for(int i = 0; i<linkedList.size(); i++) {
String value = linkedList.get(i);
System.out.println(value);
}
// Использование цикла for-each
for(String s : arrayList) {
System.out.println(s);
}
for(String s : linkedList) {
System.out.println(s);
}
هنا يجدر قول بضع كلمات عن البحث. العديد من المطورين المبتدئين، عند البحث عن عنصر في القائمة، يبدأون البحث في حلقة، ويقارنون جميع العناصر بالعنصر الذي تم البحث عنه، على الرغم من وجود الأساليب indexOf()
و lastIndexOf()
. يمكنك أيضًا استخدام الطريقة contains()
للحصول على حقيقة وجود عنصر في القائمة:
boolean isContainsSherlock = arrayList.contains("Sherlock");
boolean isContainsPhp = linkedList.contains("Php");
روابط لمزيد من القراءة
- توجد مقالة ممتازة هنا حول إزالة العناصر من ArrayList. نظرًا لأن هذه مصفوفة Java ديناميكية ، هناك العديد من التفاصيل الدقيقة في إزالة العناصر.
- يتم توضيح طريقة عمل ArrayList بالتفصيل هنا .
- المزيد عن LinkedList.
- مقالتان من حبر حول ArrayList و LinkedList .
GO TO FULL VERSION