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.COMtoUnmodifiableMap
استخدم 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 UnsupportedOperationExceptionCollectors.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.0summingInt و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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- groupingBy: تصنيف العناصر
- partitioningBy وcounting
- toMap وjoining وsummarizing
- إنشاء جامع مخصص