RxJava

2015-03-16 16 views
9

में खाली अवलोकन करने योग्य उचित रूप से संभालना मेरे पास एक ऐसी स्थिति है जहां मैं डेटाबेस से परिणामों का एक अवलोकन कर रहा हूं। मैं फिर उन्हें फ़िल्टर की श्रृंखला लागू कर रहा हूं। मेरे पास एक ग्राहक है जो परिणाम लॉग कर रहा है। यह मामला हो सकता है कि फिल्टर के बावजूद कोई भी तत्व अपना रास्ता नहीं बना सकता है। मेरा व्यवसाय तर्क बताता है कि यह कोई त्रुटि नहीं है। हालांकि, जब ऐसा होता है तो मेरा ऑनर होता है और इसमें निम्न अपवाद होता है: java.util.NoSuchElementException: Sequence contains no elementsRxJava

क्या इस तरह के अपवाद का पता लगाने और इसे अनदेखा करने के लिए स्वीकार्य अभ्यास है? या क्या इसे संभालने का एक बेहतर तरीका है?

संस्करण 1.0.0 है।

यहां एक साधारण परीक्षण मामला है जो मैं देख रहा हूं जो खुलासा करता है। यह नक्शा तक पहुंचने और कम करने से पहले सभी घटनाओं को फ़िल्टर करने से संबंधित प्रतीत होता है।

java.util.NoSuchElementException: Sequence contains no elements 
    at rx.internal.operators.OperatorSingle$1.onCompleted(OperatorSingle.java:82) 
    at rx.internal.operators.NotificationLite.accept(NotificationLite.java:140) 
    at rx.internal.operators.TakeLastQueueProducer.emit(TakeLastQueueProducer.java:73) 
    at rx.internal.operators.TakeLastQueueProducer.startEmitting(TakeLastQueueProducer.java:45) 
    at rx.internal.operators.OperatorTakeLast$1.onCompleted(OperatorTakeLast.java:59) 
    at rx.internal.operators.OperatorScan$2.onCompleted(OperatorScan.java:121) 
    at rx.internal.operators.OperatorMap$1.onCompleted(OperatorMap.java:43) 
    at rx.internal.operators.OperatorFilter$1.onCompleted(OperatorFilter.java:42) 
    at rx.internal.operators.OnSubscribeFromIterable$IterableProducer.request(OnSubscribeFromIterable.java:79) 
    at rx.internal.operators.OperatorScan$2$1.request(OperatorScan.java:147) 
    at rx.Subscriber.setProducer(Subscriber.java:139) 
    at rx.internal.operators.OperatorScan$2.setProducer(OperatorScan.java:139) 
    at rx.Subscriber.setProducer(Subscriber.java:133) 
    at rx.Subscriber.setProducer(Subscriber.java:133) 
    at rx.internal.operators.OnSubscribeFromIterable.call(OnSubscribeFromIterable.java:47) 
    at rx.internal.operators.OnSubscribeFromIterable.call(OnSubscribeFromIterable.java:33) 
    at rx.Observable$1.call(Observable.java:144) 
    at rx.Observable$1.call(Observable.java:136) 
    at rx.Observable$1.call(Observable.java:144) 
    at rx.Observable$1.call(Observable.java:136) 
    at rx.Observable$1.call(Observable.java:144) 
    at rx.Observable$1.call(Observable.java:136) 
    at rx.Observable$1.call(Observable.java:144) 
    at rx.Observable$1.call(Observable.java:136) 
    at rx.Observable$1.call(Observable.java:144) 
    at rx.Observable$1.call(Observable.java:136) 
    at rx.Observable.subscribe(Observable.java:7284) 

नीचे @davem से जवाब के आधार पर, मैं एक नया परीक्षण का मामला बनाया:

@Test 
public void test() 
{ 

    Integer values[] = new Integer[]{1, 2, 3, 4, 5}; 

    Observable.from(values).filter(new Func1<Integer, Boolean>() 
    { 
     @Override 
     public Boolean call(Integer integer) 
     { 
      if (integer < 0) 
       return true; 
      else 
       return false; 
     } 
    }).map(new Func1<Integer, String>() 
    { 
     @Override 
     public String call(Integer integer) 
     { 
      return String.valueOf(integer); 
     } 
    }).reduce(new Func2<String, String, String>() 
    { 
     @Override 
     public String call(String s, String s2) 
     { 
      return s + "," + s2; 
     } 
    }) 

      .subscribe(new Action1<String>() 
      { 
       @Override 
       public void call(String s) 
       { 
        System.out.println(s); 
       } 
      }); 
} 

क्योंकि मैं एक सुरक्षित ग्राहक का उपयोग कर रहा है, यह शुरू में एक OnErrorNotImplementedException जो निम्न अपवाद लपेटता फेंकता

@Test 
public void testFromBlockingAndSingle() 
{ 

    Integer values[] = new Integer[]{-2, -1, 0, 1, 2, 3, 4, 5}; 

    List<String> results = Observable.from(values).filter(new Func1<Integer, Boolean>() 
    { 
     @Override 
     public Boolean call(Integer integer) 
     { 
      if (integer < 0) 
       return true; 
      else 
       return false; 
     } 
    }).map(new Func1<Integer, String>() 
    { 
     @Override 
     public String call(Integer integer) 
     { 
      return String.valueOf(integer); 
     } 
    }).reduce(new Func2<String, String, String>() 
    { 
     @Override 
     public String call(String s, String s2) 
     { 
      return s + "," + s2; 
     } 
    }).toList().toBlocking().single(); 

    System.out.println("Test: " + results + " Size: " + results.size()); 

} 

और इस परीक्षण के परिणाम निम्न व्यवहार में:

जब इनपुट है:

Integer values[] = new Integer[]{-2, -1, 0, 1, 2, 3, 4, 5}; 

तो परिणाम (उम्मीद के रूप में) कर रहे हैं:

Test: [-2,-1] Size: 1 

और जब इनपुट है:

Integer values[] = new Integer[]{0, 1, 2, 3, 4, 5}; 

फिर परिणाम निम्न स्टैक ट्रेस है :

java.util.NoSuchElementException: Sequence contains no elements 
at rx.internal.operators.OperatorSingle$1.onCompleted(OperatorSingle.java:82) 
at rx.internal.operators.NotificationLite.accept(NotificationLite.java:140) 
at rx.internal.operators.TakeLastQueueProducer.emit(TakeLastQueueProducer.java:73) 
at rx.internal.operators.TakeLastQueueProducer.startEmitting(TakeLastQueueProducer.java:45) 
at rx.internal.operators.OperatorTakeLast$1.onCompleted(OperatorTakeLast.java:59) 
at rx.internal.operators.OperatorScan$2.onCompleted(OperatorScan.java:121) 
at rx.internal.operators.OperatorMap$1.onCompleted(OperatorMap.java:43) 
at rx.internal.operators.OperatorFilter$1.onCompleted(OperatorFilter.java:42) 
at rx.internal.operators.OnSubscribeFromIterable$IterableProducer.request(OnSubscribeFromIterable.java:79) 
at rx.internal.operators.OperatorScan$2$1.request(OperatorScan.java:147) 
at rx.Subscriber.setProducer(Subscriber.java:139) 
at rx.internal.operators.OperatorScan$2.setProducer(OperatorScan.java:139) 
at rx.Subscriber.setProducer(Subscriber.java:133) 
at rx.Subscriber.setProducer(Subscriber.java:133) 
at rx.internal.operators.OnSubscribeFromIterable.call(OnSubscribeFromIterable.java:47) 
at rx.internal.operators.OnSubscribeFromIterable.call(OnSubscribeFromIterable.java:33) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable$1.call(Observable.java:144) 
at rx.Observable$1.call(Observable.java:136) 
at rx.Observable.subscribe(Observable.java:7284) 
at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:441) 
at rx.observables.BlockingObservable.single(BlockingObservable.java:340) 
at EmptyTest2.test(EmptyTest2.java:19) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) 
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 
at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 
at org.junit.runner.JUnitCore.run(JUnitCore.java:160) 
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74) 
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211) 
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) 

तो ऐसा लगता है कि समस्या निश्चित रूप से कम करने के कार्य के उपयोग के साथ है।

@Test 
public void testNoReduce() 
{ 

    Integer values[] = new Integer[]{-2, -1, 0, 1, 2, 3, 4, 5}; 

    List<String> results = Observable.from(values).filter(new Func1<Integer, Boolean>() 
    { 
     @Override 
     public Boolean call(Integer integer) 
     { 
      if (integer < 0) 
       return true; 
      else 
       return false; 
     } 
    }).map(new Func1<Integer, String>() 
    { 
     @Override 
     public String call(Integer integer) 
     { 
      return String.valueOf(integer); 
     } 
    }).toList().toBlocking().first(); 

    Iterator<String> itr = results.iterator(); 
    StringBuilder b = new StringBuilder(); 

    while (itr.hasNext()) 
    { 
     b.append(itr.next()); 

     if (itr.hasNext()) 
      b.append(","); 
    } 

    System.out.println("Test NoReduce: " + b); 

} 

निम्न इनपुट के साथ:

Test NoReduce: -2,-1 

और निम्नलिखित इनपुट के साथ:

Integer values[] = new Integer[]{-2, -1, 0, 1, 2, 3, 4, 5}; 

मैं निम्नलिखित परिणाम जो उम्मीद कर रहे हैं पाने के निम्नलिखित परीक्षण का मामला है कि दोनों स्थितियों संभालती देखें :

Integer values[] = new Integer[]{0, 1, 2, 3, 4, 5}; 

मैं निम्नलिखित उत्पादन जो उम्मीद कर रहे हैं मिलता है:

Test NoReduce: 

तो, जब तक मैं पूरी तरह से गलतफहमी कुछ, एक ही रास्ता वास्तव में है कि एक फिल्टर से परिणाम जब एक नक्शे के द्वारा पीछा किया और को कम है संभाल करने के लिए एक शून्य तत्व प्रत्यक्ष कर रहा हूँ अवलोकन श्रृंखला के बाहर तर्क को कम करने के लिए। क्या आप सभी उस कथन से सहमत हैं?


अंतिम समाधान

यहाँ दोनों टॉमस ड्वोरक और डेविड Motten सुझाव को लागू करने के बाद अपने अंतिम समाधान है। मुझे लगता है कि यह समाधान उचित है।

@Test 
public void testWithToList() 
{ 

    Integer values[] = new Integer[]{-2, -1, 0, 1, 2, 3, 4, 5}; 

    Observable.from(values).filter(new Func1<Integer, Boolean>() 
    { 
     @Override 
     public Boolean call(Integer integer) 
     { 
      if (integer < 0) 
       return true; 
      else 
       return false; 
     } 
    }).toList().map(new Func1<List<Integer>, String>() 
    { 
     @Override 
     public String call(List<Integer> integers) 
     { 
      Iterator<Integer> intItr = integers.iterator(); 
      StringBuilder b = new StringBuilder(); 

      while (intItr.hasNext()) 
      { 
       b.append(intItr.next()); 

       if (intItr.hasNext()) 
       { 
        b.append(","); 
       } 
      } 

      return b.toString(); 
     } 
    }).subscribe(new Action1<String>() 
    { 
     @Override 
     public void call(String s) 
     { 
      System.out.println("With a toList: " + s); 
     } 
    }); 

} 

यहां बताया गया है कि निम्न परीक्षण दिए जाने पर यह परीक्षण कैसे व्यवहार करता है।

जब एक धारा होगा कि कुछ मान फिल्टर के माध्यम से पारित दिया:

Integer values[] = new Integer[]{-2, -1, 0, 1, 2, 3, 4, 5}; 

परिणाम है:

With a toList: -2,-1 

जब एक स्ट्रीम में कोई भी मान नहीं होगा कि दिए गए फिल्टर के माध्यम से पारित :

Integer values[] = new Integer[]{0, 1, 2, 3, 4, 5}; 

परिणाम है:

With a toList: <empty string> 
+0

कुछ कोड पोस्ट करें, तो हम स्टैक ट्रेस से यह अनुमान लगाने के लिए नहीं है। –

+0

आप सही हैं मैं एक परीक्षण केस जोड़ने में विफल रहा। मैंने एक सरल जोड़ा। – babernathy

उत्तर

13

अब आपके अपडेट के बाद त्रुटि काफी स्पष्ट है। RxJava में ReduceIllegalArgumentException के साथ विफल हो जाएगा यदि यह देखे जाने योग्य अवलोकन खाली है, बिल्कुल विनिर्देश के अनुसार (http://reactivex.io/documentation/operators/reduce.html)।

कार्यात्मक प्रोग्रामिंग में, आम तौर पर दो सामान्य ऑपरेटर होते हैं जो संग्रह को एकल मान, fold और reduce में एकत्र करते हैं। स्वीकृत शब्दावली में, fold प्रारंभिक संचयक मान लेता है, और एक फ़ंक्शन जो चलने वाले संचयक और संग्रह से मूल्य लेता है और एक अन्य संचयक मूल्य उत्पन्न करता है। स्यूडोकोड में एक उदाहरण:

[1, 2, 3, 4].fold(0, (accumulator, value) => accumulator + value)

0 के साथ शुरू होगा, और अंततः चल संचायक के लिए 1, 2, 3, 4 जोड़ने के लिए, अंत में 10, मानों का योग उपज।

कम करना बहुत समान है, केवल यह प्रारंभिक संचयक मूल्य को स्पष्ट रूप से नहीं लेता है, यह प्रारंभिक संचयक के रूप में पहले मान का उपयोग करता है, और फिर सभी शेष मूल्यों को जमा करता है। यदि आप उदास हैं तो यह समझ में आता है न्यूनतम या अधिकतम मूल्य की तलाश में है।

[1, 2, 3, 4].reduce((accumulator, value) => min(accumulator, value))

गुना को देखते हुए और अलग तरीके से, तो आप शायद fold जब भी एकत्रित मान भी खाली संग्रह (जैसे, sum में, 0 समझ में आता है) पर समझ बनाने जाएगा, और reduce अन्यथा प्रयोग करेंगे को कम (minimum बनाता है एक खाली संग्रह पर कोई समझ नहीं है, और reduce अपवाद फेंकने से आपके मामले में इस तरह के संग्रह पर काम करने में विफल रहेगा)।

आप एक समान स्ट्रिंग का उत्पादन करने के लिए एक अल्पविराम के साथ तारों के संग्रह को छेड़छाड़ करते हुए एक समान समेकन कर रहे हैं। यह थोड़ा और कठिन स्थिति है।यह शायद एक खाली संग्रह पर समझ में आता है (आप शायद खाली स्ट्रिंग की अपेक्षा करते हैं), लेकिन दूसरी तरफ, यदि आप एक खाली संचयक से शुरू करते हैं, तो आपके परिणाम की तुलना में आपके पास एक और कॉमा होगा। इसका सही समाधान यह जांचना है कि क्या संग्रह पहले खाली है, या फिर खाली संग्रह के लिए फ़ॉल बैक स्ट्रिंग वापस कर दें, या एक गैर-खाली संग्रह पर reduce करें। आप शायद देखेंगे कि अक्सर आप खाली संग्रह मामले में खाली स्ट्रिंग नहीं चाहते हैं, लेकिन "संग्रह खाली है" जैसे कुछ और अधिक उपयुक्त हो सकते हैं, इस प्रकार आपको यह आश्वस्त करना होगा कि यह समाधान एक साफ है।

Btw, मैं शब्द संग्रह यहाँ का उपयोग कर रहा नमूदार स्वतंत्र रूप से करने के बजाय, बस शैक्षिक उद्देश्यों के लिए। इसके अलावा, आरएक्सजेवा में, fold और reduce दोनों को समान कहा जाता है, reduce, केवल उस विधि के दो संस्करण हैं, केवल एक पैरामीटर लेते हैं, अन्य दो पैरामीटर।

आपके अंतिम प्रश्न के लिए: आपको अवलोकन श्रृंखला छोड़ने की ज़रूरत नहीं है। डेविड मोटेन ने सुझाव दिया है कि बस लिस्ट() का उपयोग करें।

.filter(...) 
.toList() 
.map(listOfValues => listOfValues.intersperse(", ")) 

जहां interspersereduce के मामले में लागू किया जा सकता है, अगर पहले से ही नहीं एक पुस्तकालय समारोह (यह काफी आम है)।

collection.intersperse(separator) = 
    if (collection.isEmpty()) 
     "" 
    else 
     collection.reduce(accumulator, element => accumulator + separator + element) 
+0

विस्तृत स्पष्टीकरण के लिए धन्यवाद। इससे बहुत मदद मिली। मैंने अपने अंतिम समाधान के साथ सवाल अपडेट किया। – babernathy

9

ऐसा होने का कारण यह है कि आप खाली स्ट्रीम पर toBlocking().single() का उपयोग कर रहे हैं। यदि आप स्ट्रीम से 0 या 1 मानों की अपेक्षा करते हैं तो आप toList().toBlocking().single() कर सकते हैं तो सूची में मानों का निरीक्षण करें (जो खाली हो सकता है लेकिन आप जो अपवाद प्राप्त कर रहे हैं उसे उकसाएंगे)।

+0

अच्छी नौकरी, यह बताते हुए कि केवल स्टैक ट्रेस और बिल्कुल कोई कोड नहीं है :) आपके लिए +1, -1 प्रश्न के लिए। केवल, क्या आप 'toList.toBlocking.single' के बजाय' toBlocking.toList' के नतीजे का निरीक्षण नहीं करना चाहते हैं? –

+0

धन्यवाद। 'toBlocking.toList' 1.0.8 एपीआई का हिस्सा नहीं है हालांकि' toBlocking.toIterable' का उपयोग कर सकता है। 'toBlocking.singleOrDefault' एक और संभावना है लेकिन विवरण के बिना 'toList' संस्करण इसे कवर करता है। –

+0

मैं देखता हूं, मैंने दस्तावेज़ों को नहीं पढ़ा और सोचा कि मूल 'toList' एक गलती थी, मुझे इसके अस्तित्व के बारे में भी पता नहीं था। मैं शायद '.take (1) .toBlocking.singleOrDefault' का उपयोग करता हूं, अगर मैं संसाधनों को बचाने के लिए केवल पहले तत्व के बाद था, लेकिन जैसा कि आप कहते हैं, कौन जानता है कि इस विशेष मामले के लिए क्या उचित है। –

4

आप उपयोग कर सकते हैं प्रारंभिक मूल्य संस्करण .reduce(0, (x,y) -> x+y) साथ कम करने के रूप में गुना ऑपरेटर टॉमस ड्वोरक से समझाया काम करना चाहिए