إنشاء جامع مخصص
طبّق واجهة 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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.