0Pricing
Java Academy · درس

إنشاء جامع مخصص

طبّق واجهة Collector لإنشاء تجميع قابل لإعادة الاستخدام لحالة استخدام خاصة بالمجال

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

لماذا ننشئ جامعًا مخصصًا؟

عندما لا تلائم الجوامع المضمنة مجالَك، نفّذ الواجهة Collector<T, A, R> لإنشاء منطق تجميع قابل لإعادة الاستخدام والتركيب. ‏T=الإدخال، وA=المجمّع القابل للتغيير، وR=النتيجة.

واجهة Collector

يتكوّن Collector من خمسة مكونات:

  • supplier() — تنشئ المجمّع الأولي
  • accumulator() — تضيف عنصرًا واحدًا إلى المجمّع
  • combiner() — تدمج مجمّعين، وذلك للتدفقات المتوازية
  • finisher() — تحوّل المجمّع إلى النتيجة النهائية
  • characteristics() — تلميحات لتحسين الأداء

مصنع Collector.of()

أسهل طريقة لإنشاء جامع مخصص هي استخدام Collector.of() من دون تنفيذ الواجهة مباشرة:

import java.util.stream.*;

// Collector that builds a comma-separated String
Collector<String, StringBuilder, String> csvCollector = Collector.of(
    StringBuilder::new,                     // supplier
    (sb, s) -> {
        if (sb.length() > 0) sb.append(',');
        sb.append(s);
    },                                       // accumulator
    (a, b) -> {
        if (a.length() > 0 && b.length() > 0) a.append(',');
        a.append(b); return a;
    },                                       // combiner
    StringBuilder::toString                  // finisher
);

String csv = Stream.of("Alice","Bob","Carol").collect(csvCollector);
System.out.println(csv); // Alice,Bob,Carol

جامع مخصص: TopN

أنشئ جامعًا يحتفظ فقط بأفضل N عناصر وفقًا لمقارن:

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

static <T> Collector<T, ?, List<T>> topN(int n, Comparator<T> comp) {
    return Collector.of(
        () -> new ArrayList<T>(),
        (list, e) -> {
            list.add(e);
            list.sort(comp.reversed());
            if (list.size() > n) list.remove(list.size() - 1);
        },
        (a, b) -> {
            a.addAll(b);
            a.sort(comp.reversed());
            while (a.size() > n) a.remove(a.size() - 1);
            return a;
        },
        Collector.Characteristics.UNORDERED
    );
}

استخدام جامع TopN

طبّق جامع TopN المخصص على تدفق:

List<Integer> top3 = Stream.of(5,2,9,1,7,3,8)
    .collect(topN(3, Comparator.naturalOrder()));
System.out.println(top3); // [9, 8, 7]

أعلام الخصائص

تضبط ثلاث خصائص اختيارية تحسين التدفق:

  • IDENTITY_FINISH — دالة الإنهاء هي دالة الهوية؛ لذا يمكن تخطيها
  • UNORDERED — لا يهم ترتيب النتيجة
  • CONCURRENT — يمكن استدعاء المجمّع بالتزامن

يؤدي تعيين العلم الخطأ، مثل CONCURRENT من دون سلامة الخيوط، إلى حدوث أخطاء.

جامع مخصص: المتوسط الجاري

حافظ على متوسط جارٍ من دون مرور ثانٍ:

record RunningAvg(long count, double sum) {
    RunningAvg add(double v) { return new RunningAvg(count+1, sum+v); }
    RunningAvg merge(RunningAvg o) { return new RunningAvg(count+o.count, sum+o.sum); }
    double avg() { return count == 0 ? 0 : sum / count; }
}

Collector<Double, RunningAvg[], Double> avgCollector = Collector.of(
    () -> new RunningAvg[]{new RunningAvg(0, 0)},
    (a, v) -> a[0] = a[0].add(v),
    (a, b) -> { a[0] = a[0].merge(b[0]); return a; },
    a -> a[0].avg()
);

double avg = Stream.of(10.0, 20.0, 30.0).collect(avgCollector);
System.out.println(avg); // 20.0

تنفيذ واجهة Collector مباشرةً

لمزيد من التحكم، نفّذ Collector<T,A,R> كفئة:

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

class StringJoiningCollector implements Collector<String, List<String>, String> {
    private final String delimiter;
    StringJoiningCollector(String delimiter) { this.delimiter = delimiter; }
    public Supplier<List<String>> supplier() { return ArrayList::new; }
    public BiConsumer<List<String>, String> accumulator() { return List::add; }
    public BinaryOperator<List<String>> combiner() {
        return (a,b) -> { a.addAll(b); return a; };
    }
    public Function<List<String>, String> finisher() {
        return list -> String.join(delimiter, list);
    }
    public Set<Characteristics> characteristics() { return Set.of(); }
}

مقارنة الجامع المخصص بالجامع المضمن

قبل كتابة جامع مخصص، تحقّق مما إذا كان تركيب الجوامع المضمنة يحقق النتيجة نفسها، فهي مختبرة ومحسّنة مسبقًا:

// Instead of custom, compose:
Map<String, IntSummaryStatistics> stats = orders.stream().collect(
    Collectors.groupingBy(Order::status,
        Collectors.summarizingInt(Order::amount))
);
// This replaces many custom collector implementations

سلامة الخيوط في الجوامع المخصصة

عند تعيين خاصية CONCURRENT، يُستدعى المجمّع من عدة خيوط في الوقت نفسه. لذلك يجب أن يكون المجمّع آمنًا للاستخدام مع الخيوط، مثل استخدام ConcurrentHashMap أو AtomicLong. ومن دون CONCURRENT، يتولى combiner معالجة التوازي بأمان.

حالة استخدام فعلية: جامع المدرج التكراري

صنّف القيم الرقمية ضمن مجموعات في خريطة مدرج تكراري:

Collector<Integer, Map<Integer, Long>, Map<Integer, Long>> histogram =
    Collector.of(
        LinkedHashMap::new,
        (map, val) -> map.merge(val / 10 * 10, 1L, Long::sum),
        (a, b) -> { b.forEach((k,v) -> a.merge(k,v,Long::sum)); return a; },
        Collector.Characteristics.IDENTITY_FINISH
    );

Map<Integer,Long> hist = Stream.of(5,15,25,12,8,22,18,3)
    .collect(histogram);
hist.forEach((bucket, cnt) -> System.out.println(bucket+"-"+(bucket+9)+": "+cnt));

تحقق سريع

أي علم من أعلام Collector.Characteristics يشير إلى أن دالة الإنهاء هي تحويل الهوية ويمكن تخطيها؟

مراجعة: الجوامع المخصصة

أهم النقاط:

  • Collector<T,A,R>: ‏T=الإدخال، وA=المجمّع، وR=النتيجة
  • استخدم Collector.of() للتنفيذ السريع من دون إنشاء فئة
  • تتكون الواجهة من الأجزاء الخمسة supplier وaccumulator وcombiner وfinisher وcharacteristics
  • ‏IDENTITY_FINISH وUNORDERED وCONCURRENT أعلام لتحسين الأداء
  • فضّل تركيب الجوامع المضمنة، ولا تلجأ إلى جامع مخصص إلا عند الحاجة

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

هل درس «إنشاء جامع مخصص» مجاني؟

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

ماذا ستتعلم في «إنشاء جامع مخصص»؟

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

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

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

كم من الوقت يستغرق درس «إنشاء جامع مخصص»؟

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

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

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

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

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