2016-01-13 4 views
6

के साथ विशिष्ट विशेषताओं द्वारा गतिशील समूहांकन I जावा 8 संग्रह-स्ट्रीम का उपयोग कर, मल्टीप्लट विशेषताओं द्वारा ऑब्जेक्ट्स की एक सूची को समूहबद्ध करने का प्रयास कर रहा हूं।संग्रह.स्ट्रीम

यह बहुत अच्छी तरह से काम करता है:

public class MyClass 
{ 
    public String title; 
    public String type; 
    public String module; 
    public MyClass(String title, String type, String module) 
    { 
     this.type = type; 
     this.title = title; 
     this.module= module; 
    } 
} 

List<MyClass> data = new ArrayList(); 
data.add(new MyClass("1","A","B")); 
data.add(new MyClass("2","A","B")); 
data.add(new MyClass("3","A","C")); 
data.add(new MyClass("4","B","A")); 

Object result = data.stream().collect(Collectors.groupingBy((MyClass m) 
-> m.type, Collectors.groupingBy((MyClass m) -> m.module))); 

लेकिन मैं इसे थोड़ा और अधिक गतिशील बनाने के लिए करना चाहते हैं। मैं सिर्फ एक स्ट्रिंग-ऐरे (या सूची) निर्दिष्ट करना चाहता हूं जिसका उपयोग GroupBy में किया जाना चाहिए।

कुछ की तरह:

Object groupListBy(List data, String[] groupByFieldNames) 
{ 
    //magic code 
} 

और मैं कॉल करना चाहते हैं:

groupListBy(data, new String[]{"type","module"}); 

मैं अपने उदाहरण में GroupBy-विधि और अधिक गतिशील बना सकते हैं, पसंद है?

उत्तर

10

उस कोड को और अधिक गतिशील बनाने के साथ मुख्य समस्या यह है कि आप पहले से नहीं जानते कि समूह द्वारा कितने तत्व होंगे। ऐसे मामले में, सभी तत्वों के List द्वारा समूह करना सबसे अच्छा है। यह काम करता है क्योंकि दो सूचियां बराबर होती हैं यदि उनके सभी तत्व समान हैं और उसी क्रम में हैं।

इस मामले में, प्रकार और फिर मॉड्यूल द्वारा समूहित करने के बजाय, हम प्रत्येक डेटा प्रकार और मॉड्यूल से युक्त सूची में समूह करेंगे।

private static Map<List<String>, List<MyClass>> groupListBy(List<MyClass> data, String[] groupByFieldNames) { 
    final MethodHandles.Lookup lookup = MethodHandles.lookup(); 
    List<MethodHandle> handles = 
     Arrays.stream(groupByFieldNames) 
       .map(field -> { 
        try { 
         return lookup.findGetter(MyClass.class, field, String.class); 
        } catch (Exception e) { 
         throw new RuntimeException(e); 
        } 
       }).collect(toList()); 
    return data.stream().collect(groupingBy(
      d -> handles.stream() 
         .map(handle -> { 
          try { 
           return (String) handle.invokeExact(d); 
          } catch (Throwable e) { 
           throw new RuntimeException(e); 
          } 
         }).collect(toList()) 
     )); 
} 

कोड के पहले भाग MethodHandle के List में फ़ील्ड नाम की सरणी बदल देती है। प्रत्येक क्षेत्र के लिए, एक MethodHandle है कि क्षेत्र के लिए लिया गया है:

का उत्पादन एक विधि संभाल देने के लिए एक गैर करने के लिए उपयोग पढ़ें: इस findGetter साथ भी क्षेत्र नाम के लिए एक संभाल MethodHandles.lookup() से देखने को प्राप्त करने और ऊपर देखकर भी किया जाता है -स्टैटिक फ़ील्ड

शेष कोड क्लासिफायर को समूह से बनाता है। String मान की सूची वापस करने के लिए डेटा उदाहरण पर सभी हैंडल लगाए जाते हैं। यह Stream वर्गीकरण के रूप में कार्य करने के लिए List में एकत्र किया गया है।

नमूना कोड:

public static void main(String[] args) { 
    List<MyClass> data = new ArrayList<>(); 
    data.add(new MyClass("1", "A", "B")); 
    data.add(new MyClass("2", "A", "B")); 
    data.add(new MyClass("3", "A", "C")); 
    data.add(new MyClass("4", "B", "A")); 

    System.out.println(groupListBy(data, new String[] { "type", "module" })); 
} 

आउटपुट:

{[B, A]=[4], [A, B]=[1, 2], [A, C]=[3]} 

जब MyClass.toString() केवल title वापस जाने के लिए ओवरराइड है।

5

नामों की सूची के बजाय, आप अपने तत्वों को समूहित करने के लिए कार्यों की एक सूची (एक अनिवार्य) की आपूर्ति करने पर भी विचार कर सकते हैं।

इन कार्यों को किसी ऑब्जेक्ट पर MyClass का एक तत्व मैप करना चाहिए, ताकि आप Function<MyClass, ?> का उपयोग कर सकें।

private static Map<List<Object>, List<MyClass>> groupListBy(List<MyClass> data, Function<MyClass, ?> mandatory, Function<MyClass, ?>... others) { 
    return data.stream() 
       .collect(groupingBy(cl -> Stream.concat(Stream.of(mandatory), Stream.of(others)).map(f -> f.apply(cl)).collect(toList()))); 
} 

और कॉल के कुछ उदाहरण:

groupListBy(data, m -> m.type); //group only by type 
groupListBy(data, m -> m.type, m -> m.module); //group by type and by module 

बेशक आप इस विधि का सामान्य इतना है कि यह प्रकार U -> Object के कार्यों के साथ एक Map<List<Object>, List<U>> रिटर्न कमा सकते हैं।