0Pricing
Java Academy · درس

toMap وjoining وsummarizing

حوّل التدفقات إلى خرائط، وادمج السلاسل النصية باستخدام joining، واحسب الإحصاءات باستخدام summarizingInt

toMap وjoining وsummarizing درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

أساسيات Collectors.toMap

تحوّل Collectors.toMap(keyMapper, valueMapper) التدفق إلى Map. ويوفّر كل عنصر زوجًا واحدًا من مفتاح وقيمة.

import java.util.*;
import java.util.stream.*;

record Country(String code, String name) {}
List<Country> countries = List.of(
    new Country("US","United States"),
    new Country("DE","Germany"),
    new Country("JP","Japan")
);

Map<String, String> codeToName =
    countries.stream().collect(
        Collectors.toMap(Country::code, Country::name)
    );
System.out.println(codeToName.get("DE")); // Germany

معالجة المفاتيح المكررة

ترمي toMap استثناء IllegalStateException عند وجود مفاتيح مكررة. مرّر دالة دمج كوسيطة ثالثة لحل التعارضات:

List<String> words = List.of("apple","ant","banana","bat","cherry");

Map<Character, String> firstByLetter =
    words.stream().collect(
        Collectors.toMap(
            w -> w.charAt(0),
            w -> w,
            (existing, replacement) -> existing // keep first
        )
    );
System.out.println(firstByLetter); // {a=apple, b=banana, c=cherry}

استخدام toMap مع تحويل القيم

يمكن أن تكون دالة تحويل القيمة أي دالة، ما يتيح تحويل العنصر قبل تخزينه:

record User(String login, String email) {}
List<User> users = List.of(
    new User("alice","alice@example.com"),
    new User("bob","bob@example.com")
);

Map<String, String> loginToEmail =
    users.stream().collect(
        Collectors.toMap(User::login, u -> u.email().toUpperCase())
    );
System.out.println(loginToEmail.get("alice")); // ALICE@EXAMPLE.COM

toUnmodifiableMap

استخدم Collectors.toUnmodifiableMap() لإنشاء خريطة غير قابلة للتعديل، وهو مكافئ لتغليف الخريطة باستخدام Collections.unmodifiableMap()، لكن بطريقة أوضح:

Map<String, Integer> wordLengths =
    Stream.of("Java","Stream","API")
          .collect(Collectors.toUnmodifiableMap(w -> w, String::length));
// wordLengths.put("test",4); // throws UnsupportedOperationException

Collectors.joining

تضم Collectors.joining() العناصر النصية إلى بعضها. وتقبل فاصلًا وبادئة ولاحقة اختيارية:

List<String> names = List.of("Alice","Bob","Carol");

System.out.println(names.stream().collect(Collectors.joining()));
// AliceBobCarol

System.out.println(names.stream().collect(Collectors.joining(", ")));
// Alice, Bob, Carol

System.out.println(names.stream().collect(Collectors.joining(", ", "[", "]")));
// [Alice, Bob, Carol]

استخدام joining لإخراج CSV

أنشئ صفوف CSV أو عبارات SQL IN باستخدام joining:

List<Integer> ids = List.of(1,2,3,4,5);

String inClause = ids.stream()
    .map(Object::toString)
    .collect(Collectors.joining(",", "(", ")"));
System.out.println("WHERE id IN " + inClause);
// WHERE id IN (1,2,3,4,5)

summarizingInt / Long / Double

يحسب Collectors.summarizingInt() جميع الإحصاءات في مرور واحد ويعيد كائن IntSummaryStatistics:

record Product(String name, int price) {}
List<Product> products = List.of(
    new Product("A",100), new Product("B",200), new Product("C",150)
);

IntSummaryStatistics stats =
    products.stream().collect(Collectors.summarizingInt(Product::price));

System.out.println("Count: " + stats.getCount());   // 3
System.out.println("Sum: "   + stats.getSum());     // 450
System.out.println("Min: "   + stats.getMin());     // 100
System.out.println("Max: "   + stats.getMax());     // 200
System.out.println("Avg: "   + stats.getAverage()); // 150.0

summingInt وaveragingInt

في الحالات الأبسط التي تحتاج فيها إلى إحصاء واحد فقط:

int total = products.stream().collect(Collectors.summingInt(Product::price));
System.out.println("Total: " + total); // 450

double avg = products.stream().collect(Collectors.averagingInt(Product::price));
System.out.println("Average: " + avg); // 150.0

استخدام groupingBy مع summarizingInt

ادمج التلخيص لكل مجموعة:

record Sale(String region, int amount) {}
List<Sale> sales = List.of(
    new Sale("North",100), new Sale("South",200),
    new Sale("North",150), new Sale("South",300)
);

Map<String, IntSummaryStatistics> statsByRegion =
    sales.stream().collect(
        Collectors.groupingBy(Sale::region,
            Collectors.summarizingInt(Sale::amount))
    );
statsByRegion.forEach((r,s) ->
    System.out.printf("%s: sum=%d avg=%.1f%n", r, s.getSum(), s.getAverage()));

التجميع في LinkedHashMap

ينتج toMap كائن HashMap غير مرتب افتراضيًا. استخدم التحميل الزائد ذي الوسائط الأربعة لتحديد LinkedHashMap والحفاظ على ترتيب الإدراج:

Map<String, Integer> ordered =
    Stream.of("banana","apple","cherry")
          .collect(Collectors.toMap(
              w -> w,
              String::length,
              (a,b) -> a,
              LinkedHashMap::new
          ));
System.out.println(ordered.keySet()); // [banana, apple, cherry]

تطبيق عملي: محلل الإعدادات

حلّل الأسطر من الشكل key=value إلى خريطة باستخدام toMap:

List<String> lines = List.of("host=localhost","port=5432","db=myapp");

Map<String, String> config =
    lines.stream()
         .map(l -> l.split("=", 2))
         .collect(Collectors.toMap(a -> a[0], a -> a[1]));

System.out.println(config.get("port")); // 5432

تحقق سريع

استخدمت Collectors.toMap(k, v) على تدفق يحتوي على عنصرين لهما المفتاح نفسه. ماذا يحدث؟

مراجعة: toMap وjoining وsummarizing

أهم النقاط:

  • toMap(keyMapper, valueMapper) — يرمي استثناءً عند وجود مفاتيح مكررة من دون دالة دمج
  • joining(delimiter, prefix, suffix) — يضم السلاسل النصية بطريقة أنيقة
  • يوفّر summarizingInt العدد والمجموع والقيمة الصغرى والقيمة الكبرى والمتوسط في مرور واحد
  • استخدم summingInt/averagingInt عند الحاجة إلى إحصاء واحد
  • استخدم toMap ذي الوسائط الأربعة مع LinkedHashMap::new للحصول على نتائج مرتبة

الأسئلة الشائعة

هل درس «toMap وjoining وsummarizing» مجاني؟

نعم — نص درس «toMap وjoining وsummarizing» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «toMap وjoining وsummarizing»؟

حوّل التدفقات إلى خرائط، وادمج السلاسل النصية باستخدام joining، واحسب الإحصاءات باستخدام summarizingInt تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «toMap وjoining وsummarizing»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. groupingBy: تصنيف العناصر
  2. partitioningBy وcounting
  3. toMap وjoining وsummarizing
  4. إنشاء جامع مخصص
← العودة إلى Java Academy