2016-03-10 3 views
5

मान लीजिए कि मेरे पास अंतराल की सूची है (प्रारंभ से क्रमबद्ध) और मैं उन्हें तोड़ना चाहता हूं ताकि मेरे पास अंतराल के ओवरलैपिंग समूहों की सूची हो। तो, उदाहरण के लिए, साथ Interval के रूप में:पिछली तत्वों से संबंधित स्थिति में समूहों में जावा 8 विभाजन सूची

public class Interval { 
    private final int start; 
    private final int end; 

    public Interval(int start,int end){ 
     this.start = start; 
     this.end = end; 
    } 

    public int getStart(){return start;} 
    public int getEnd(){return end;} 

    public String toString(){ return "("+start+","+end+")"; } 
} 

और एक List<Interval> की तरह:

[(0,4),(1,7),(6,10),(13,17),(20,100),(22,31),(60,65)] 

मैं List<List<Interval>> का उत्पादन करना चाहते हैं:

[[(0,4),(1,7),(6,10)],[(13,17)],[(20,100),(22,31),(60,65)]] 

मैं इस अप कोड कर सकते हैं, लेकिन मैं मैं वास्तव में जावा 8 के अधिक कार्यात्मक दृष्टिकोण का आनंद ले रहा हूं, और जानना चाहता हूं कि जावा 8 स्ट्रीम का उपयोग करके ऐसा करने के लिए एक बेवकूफ तरीका है या नहीं।

मैंने Collectors प्रदान की गई "समूह द्वारा" शैलियों पर एक नज़र डाली है, लेकिन वे लागू नहीं होते हैं क्योंकि मैं वास्तव में क्लासिफायर द्वारा समूहबद्ध नहीं कर रहा हूं- आप केवल समूह पर आधारित समूहों की गणना नहीं कर सकते प्रत्येक व्यक्तिगत तत्व की एक संपत्ति, आपको अब तक गणना की गई समूहों के संबंध में प्रत्येक तत्व के गुणों पर विचार करना होगा।

निश्चित रूप से कार्यात्मक भाषाओं में ऐसा करने के लिए गैर-पागल तरीका है (हालांकि मैं किसी ऐसे व्यक्ति के रूप में बोलता हूं जो वास्तव में एक कार्यात्मक प्रोग्रामर नहीं है :-))। जावा 8 में धाराओं के साथ मैं इसे कैसे कर सकता हूं?

+2

मुझे लगता है, 'this.start = end;' वह नहीं है जो आप चाहते हैं। लेकिन यह एक अच्छी बात है कि आप 'अंतिम' चर का उपयोग कर रहे हैं, इसलिए त्रुटि तुरंत कंपाइलर द्वारा स्पॉट की जाती है। – Holger

+1

वैसे, आउटपुट क्या होना चाहिए जब इनपुट निम्न है: '[(60, 65), (22, 31), (20, 100)]'? क्या सभी तीन अंतराल एक साथ विलय हो जाना चाहिए? दूसरे शब्दों में, इनपुट अंतराल का क्रम परिणाम बदल सकता है? –

+1

@ टगीर वैलेव: प्रश्न की पूर्व शर्त (पहली वाक्य में) यह है कि तत्वों को उनके प्रारंभ बिंदु से क्रमबद्ध किया जाता है। किसी भी समाधान के लिए सॉर्टिंग चरण तैयार करके उस आवश्यकता को आराम करना आसान है। – Holger

उत्तर

4

आप नहीं कर सकते। स्ट्रीम इस तरह की समस्या के लिए उपयुक्त नहीं हैं; धाराओं में "पिछले तत्व" की कोई धारणा नहीं है और उन्हें मनमानी क्रम में तत्वों पर काम करने की अनुमति है। आप इसे जावा में कर सकते हैं, निश्चित रूप से, और आप इसे कार्यात्मक भाषाओं में कर सकते हैं, लेकिन इसका मतलब यह नहीं है कि धाराएं कार्यात्मक भाषा डेटा संरचनाओं की तरह काम करती हैं जिनका आप उपयोग कर रहे हैं।

+0

** अपवर्तित होगा लेकिन प्रतिनिधि की कमी – codeCogs

+2

सभी परिचालनों को मनमानी क्रम में तत्वों को संसाधित करने की अनुमति नहीं है। मनमाने ढंग से आदेश देने से लागू करना होगा, उदा। 'Collect.toList()', बहुत कठिन ... – Holger

+0

लेकिन tolist मनमानी क्रम में तत्वों को अनुमति देता है, यह सिर्फ एक आदेशित फैशन में जमाकर्ताओं को विलय करता है। –

4

groupingBy कलेक्टरों का अध्ययन करते समय आप सही जगह पर देख रहे थे, लेकिन आप यह भी सही हैं कि वे अंतराल विलय के लिए आवश्यक तर्क प्रदान नहीं करेंगे। लेकिन वे अवधारणात्मक रूप से तत्वों को पिछले तत्वों द्वारा बनाए गए राज्य में विलय कर रहे हैं। आपको अपने आप को एक समान संग्राहक को लागू करना होगा।

आपके विनिर्देशन कि तत्वों को पहले ही अपनी शुरुआत सूचकांक द्वारा presorted कर रहे हैं पर निर्भर, आप इसे पसंद कर सकते हैं:

Comparator<Interval> byStart = Comparator.comparingInt(Interval::getStart); 
Comparator<Interval> byEnd = Comparator.comparingInt(Interval::getEnd); 
Collection<List<Interval>> merged = intervalList.stream().collect(
     () -> new TreeMap<Interval,List<Interval>>(byStart), 
     (map,i) -> { 
      Map.Entry<Interval,List<Interval>> e=map.floorEntry(i); 
      if(e!=null && Collections.max(e.getValue(), byEnd).getEnd()>=i.getStart()) 
       e.getValue().add(i); 
      else map.computeIfAbsent(i, x->new ArrayList<>()).add(i); 
     }, 
     (m1,m2) -> m2.forEach((i,list) -> { 
      Map.Entry<Interval,List<Interval>> e=m1.floorEntry(i); 
      if(e!=null && Collections.max(e.getValue(), byEnd).getEnd()>=i.getStart()) 
       e.getValue().addAll(list); 
      else m1.put(i, list); 
     }) 
    ).values(); 

यह बजाय एक List की तुलना में एक Collection बनाता है, लेकिन आप बस एक List से बाहर बना सकते हैं यह:

List<List<Interval>> list = new ArrayList<>(merged); 

आपको लगता है कि निश्चित रूप से यदि आप एक लंबे समय के लिए परिणाम रखने के बजाय इसे तुरंत प्रसंस्करण करने का इरादा के रूप में Collection लौटे करना चाहिए कलेक्टर द्वारा TreeMap में आवश्यकतानुसार अधिक संसाधन रखने का एक दृश्य है।

मुझे लगता है कि ज्यादातर मामलों में आप लूप आधारित समाधान के साथ बेहतर होते हैं।

+0

अच्छा समाधान! (1) मुझे लगता है कि संयोजक बेकार है अगर जमाकर्ता हर धारा तत्व के माध्यम से क्रमशः चलता है (यदि यह समाधान काम करने जा रहा है तो यह मामला होना चाहिए)। (2) सहमत: इस मामले में एक लूप अधिक पारदर्शी और कुशल होने की संभावना है। – codeCogs

+1

@codeCogs: combiner का अनुक्रमिक धारा के लिए उपयोग नहीं किया जाएगा, लेकिन यह भविष्य में आश्चर्य की बातों से बचने के लिए हमेशा एक काम करने वाला संयोजन प्रदान करने के लिए एक अच्छी कोडिंग शैली है। औपचारिक रूप से, ऐसा नहीं करना एपीआई अनुबंध का भी उल्लंघन है। इस समाधान का संयोजक सही तरीके से काम करता है, लेकिन इसे प्राप्त करने के लिए बहुत अधिक लंबाई तक जाता है, जो समानांतर प्रसंस्करण के किसी भी लाभ को खा सकता है, यदि कोई भी शुरू हो रहा है ... – Holger

संबंधित मुद्दे