البحث عن العنصر
الآن أصبحنا على دراية بهذه الطريقة المصممة بأناقة لتحويل المجموعات، لكنها غير مجدية في العثور على العناصر. لكن طريقة التصفية ولدت لهذا الغرض.
نريد الآن إزالة الأسماء التي تبدأ بحرف N من قائمة الأسماء. بالطبع قد لا يكون هناك أي شيء، وقد تكون النتيجة مجموعة فارغة. دعونا ننفذها أولاً باستخدام الطريقة القديمة.
انسخ رمز الكود كما يلي:
Final List<String> beginWithN = new ArrayList<String>();
لـ (اسم السلسلة: الأصدقاء) {
إذا (name.startsWith("N")) {
startWithN.add(name);
}
}
إن كتابة الكثير من التعليمات البرمجية لمثل هذا الحدث البسيط أمر مطول للغاية. نقوم أولاً بإنشاء متغير ثم نقوم بتهيئته إلى مجموعة فارغة. ثم قم بالمراجعة عبر المجموعة الأصلية وابحث عن تلك الأسماء التي تبدأ بالحرف المحدد. إذا وجدت، يتم إدراجها في المجموعة.
دعونا نستخدم طريقة التصفية لإعادة بناء الكود أعلاه لنرى مدى قوته.
انسخ رمز الكود كما يلي:
القائمة النهائية <سلسلة> تبدأ بـ N =
friends.stream()
.filter(name -> name.startsWith("N"))
.collect(Collectors.toList());
يتلقى أسلوب التصفية تعبير لامدا الذي يُرجع قيمة منطقية. إذا تم تقييم التعبير على أنه صحيح، فسيتم إضافة هذا العنصر في سياق التنفيذ إلى مجموعة النتائج؛ وإذا لم يكن الأمر كذلك، فسيتم تخطيه. ما تم إرجاعه أخيرًا هو Steam، والذي يحتوي فقط على تلك العناصر التي يعود تعبيرها صحيحًا. أخيرًا نستخدم طريقة التجميع لتحويل المجموعة إلى قائمة - سنناقش هذه الطريقة بمزيد من التعمق في استخدام طريقة التجميع وفئة Collectors في الصفحة 52.
لنطبع العناصر الموجودة في مجموعة النتائج هذه:
انسخ رمز الكود كما يلي:
System.out.println(String.format("تم العثور على %d من الأسماء"، beginWithN.size()));
يتضح من الإخراج أن هذه الطريقة وجدت جميع العناصر المطابقة في المجموعة.
انسخ رمز الكود كما يلي:
تم العثور على 2 أسماء
طريقة التصفية، مثل طريقة الخريطة، تقوم أيضًا بإرجاع مكرر، ولكن هذا كل ما في الأمر. المجموعة التي يتم إرجاعها بواسطة الخريطة هي بنفس حجم مجموعة الإدخال، ولكن من الصعب تحديد ما الذي سيرجعه المرشح. نطاق حجم المجموعة التي تقوم بإرجاعها، من 0 إلى عدد العناصر في مجموعة الإدخال. على عكس الخريطة، يقوم عامل التصفية بإرجاع مجموعة فرعية من مجموعة الإدخال.
حتى الآن، نحن راضون جدًا عن بساطة التعليمات البرمجية التي توفرها تعبيرات لامدا، ولكن إذا لم نكن حذرين، فستبدأ مشكلة تكرار التعليمات البرمجية في النمو ببطء. دعونا نناقش هذه المسألة أدناه.
إعادة استخدام تعبيرات لامدا
تبدو تعبيرات Lambda موجزة للغاية، ولكن في الواقع من السهل جعل التعليمات البرمجية زائدة عن الحاجة إذا لم تكن حذرًا. سيؤدي التكرار إلى انخفاض جودة التعليمات البرمجية وصعوبة الصيانة؛ إذا أردنا إجراء تغيير، فيجب علينا تغيير العديد من الرموز ذات الصلة معًا.
يمكن أن يساعدنا تجنب التكرار أيضًا على تحسين الأداء. يتم تركيز الكود ذي الصلة في مكان واحد، حتى نتمكن من تحليل أدائه ومن ثم تحسين الكود هنا، مما يمكنه بسهولة تحسين أداء الكود.
الآن دعونا نلقي نظرة على السبب الذي يجعل استخدام تعبيرات لامدا يؤدي بسهولة إلى تكرار التعليمات البرمجية، ونفكر في كيفية تجنب ذلك.
انسخ رمز الكود كما يلي:
القائمة النهائية <String> friends =
Arrays.asList("Brian"، "Nate"، "Neal"، "Raju"، "Sara"، "Scott");
محرري القائمة النهائية <String> =
Arrays.asList("بريان"، "جاكي"، "جون"، "مايك")؛
القائمة النهائية<String> الرفاق =
Arrays.asList("Kate"، "Ken"، "Nick"، "Paula"، "Zach");
نريد تصفية الأسماء التي تبدأ بحرف معين.
نريد تصفية الأسماء التي تبدأ بحرف معين. دعونا ننفذها ببساطة باستخدام طريقة التصفية أولاً.
انسخ رمز الكود كما يلي:
العد الطويل النهائي FriendsStartN =
friends.stream()
.filter(name -> name.startsWith("N")).count();
العد الطويل النهائيEditorsStartN =
Editors.stream()
.filter(name -> name.startsWith("N")).count();
العد الطويل النهائيComradesStartN =
رفاق. تيار ()
.filter(name -> name.startsWith("N")).count();
تجعل تعبيرات Lambda التعليمات البرمجية تبدو موجزة، ولكنها تؤدي دون قصد إلى تكرار التعليمات البرمجية. في المثال أعلاه، إذا أردنا تغيير تعبير لامدا، علينا تغيير أكثر من مكان - وهذا غير ممكن. لحسن الحظ، يمكننا تعيين تعبيرات لامدا للمتغيرات وإعادة استخدامها تمامًا مثل الكائنات.
يتلقى أسلوب التصفية، مستقبل تعبير لامدا، إشارة إلى الواجهة الوظيفية java.util.function.Predicate. هنا، يصبح برنامج التحويل البرمجي Java مفيدًا مرة أخرى، حيث يقوم بإنشاء تطبيق لطريقة اختبار Predicate باستخدام تعبير lambda المحدد. يمكننا الآن أن نطلب بشكل أكثر وضوحًا من مترجم Java إنشاء هذه الطريقة بدلاً من إنشائها حيث يتم تعريف المعلمات. في المثال أعلاه، يمكننا تخزين تعبير لامدا بشكل صريح في مرجع من النوع المسند، ثم تمرير هذا المرجع إلى طريقة التصفية، وهذا يمكن بسهولة تجنب تكرار التعليمات البرمجية.
دعونا نعيد صياغة الكود السابق لجعله متوافقًا مع مبدأ DRY. (لا تكرر نفسك – المبدأ الجاف – يرجى الرجوع إلى كتاب المبرمج العملي: من العامل الماهر إلى المعلم [HT00]).
انسخ رمز الكود كما يلي:
المسند النهائي<String> beginWithN = name -> name.startsWith("N");
العد الطويل النهائي FriendsStartN =
friends.stream()
.تصفية(يبدأ معN)
.عدد()؛
العد الطويل النهائيEditorsStartN =
Editors.stream()
.تصفية(يبدأ معN)
.عدد()؛
العد الطويل النهائيComradesStartN =
رفاق. تيار ()
.تصفية(يبدأ معN)
.عدد()؛
الآن بدلاً من كتابة تعبير لامدا مرة أخرى، نكتبه مرة واحدة ونخزنه في مرجع من النوع المسند يسمى startWithN. في استدعاءات التصفية الثلاثة التالية، رأى مترجم Java تعبير لامدا متنكرًا في شكل مسند، وابتسم وقبله بصمت.
هذا المتغير الذي تم تقديمه حديثًا يلغي تكرار التعليمات البرمجية بالنسبة لنا. لكن لسوء الحظ، كما سنرى لاحقًا، سيعود العدو قريبًا للانتقام، دعونا نرى ما هي الأسلحة الأقوى التي يمكن أن تدمرهم لنا.