【问题标题】:RxJava: How to convert List of objects to List of another objectsRxJava:如何将对象列表转换为另一个对象列表
【发布时间】:2020-04-06 05:04:11
【问题描述】:

我有 SourceObjects 列表,我需要将其转换为 ResultObjects 列表。

我可以使用 ResultObject 的方法将一个对象获取到另一个对象:

convertFromSource(srcObj);

当然可以这样:

public void onNext(List<SourceObject> srcObjects) {
   List<ResultsObject> resObjects = new ArrayList<>();
   for (SourceObject srcObj : srcObjects) {
       resObjects.add(new ResultsObject().convertFromSource(srcObj));
   }
}

但我会非常感谢能够展示如何使用 rxJava 做同样事情的人。

【问题讨论】:

    标签: java android rx-java


    【解决方案1】:

    如果您的Observable 发出List,您可以使用这些运算符:

    • flatMapIterable(将您的列表转换为可观察的项目)
    • map(将您的项目转换为另一个项目)
    • toList 操作符(将完成的 Observable 转换为从完成的 Observable 发出项目列表的 Observable)

      Observable<SourceObjet> source = ...
      source.flatMapIterable(list -> list)
            .map(item -> new ResultsObject().convertFromSource(item))
            .toList()
            .subscribe(transformedList -> ...);
      

    【讨论】:

    • 这看起来很干净,虽然有一些缺点。 1. 如果 source 发出多个列表,它们都将被flatMapIterable 扁平化为一个对象流,toList() 会将它们收集在一个列表中。因此,如果您关心多个排放并将它们保存在单独的列表中,那么不幸的是您会失去它。 2. toList() 等待源 observable 完成,所以如果你在无限的 observable 上执行此操作(比如绑定到 UI 事件),你将永远不会在 subscribe() 中得到任何东西。
    • @MarcinKoziński 你将如何解决 nr。 2?我遇到 .toList 不返回任何内容的情况。谢谢
    • 我认为您只需要使用不同的方法。如果您尝试转换列表的每个元素,您可能只需要执行.map(list -&gt; /* build a new list using plain old for loop */)。或者使用任何其他方法在.map() 中转换您的列表。否则,你可以在你的无限 observable 上使用像 .take(itemCount) 这样的运算符来使其有限。请注意,这显然会删除itemCount 之后的所有元素,因此这可能不是您想要的。
    • “一些缺点” - 相反,我想说这几乎总是不想要的。这个答案应该被否决,而诺埃尔的答案应该被投赞成票。
    • 非常感谢先生,为行话免费解释。超级有帮助。
    【解决方案2】:

    如果你想保持源Observable发出的Lists,但将其内容,即Observable&lt;List&lt;SourceObject&gt;&gt;转换为Observable&lt;List&lt;ResultsObject&gt;&gt;,你可以这样做:

    Observable<List<SourceObject>> source = ...
    source.flatMap(list ->
            Observable.fromIterable(list)
                .map(item -> new ResultsObject().convertFromSource(item))
                .toList()
                .toObservable() // Required for RxJava 2.x
        )
        .subscribe(resultsList -> ...);
    

    这确保了几件事:

    • Observable 发出的Lists 的数量保持不变。即如果源发出 3 个列表,则另一端将有 3 个转换后的列表
    • 使用Observable.fromIterable() 将确保内部Observable 终止,以便可以使用toList()

    【讨论】:

      【解决方案3】:

      Observable.from() 工厂方法允许您将对象集合转换为 Observable 流。一旦你有了一个流,你就可以使用map 操作符来转换每个发射的项目。最后,您必须订阅生成的 Observable 才能使用转换后的项目:

      // Assuming List<SourceObject> srcObjects
      Observable<ResultsObject> resultsObjectObservable = Observable.from(srcObjects).map(new Func1<SourceObject, ResultsObject>() {
          @Override
          public ResultsObject call(SourceObject srcObj) {
              return new ResultsObject().convertFromSource(srcObj);
          }
      });
      
      resultsObjectObservable.subscribe(new Action1<ResultsObject>() { // at this point is where the transformation will start
          @Override
          public void call(ResultsObject resultsObject) { // this method will be called after each item has been transformed
              // use each transformed item
          }
      });
      

      如果您使用 lambda,则缩写版本如下所示:

      Observable.from(srcObjects)
        .map(srcObj -> new ResultsObject().convertFromSource(srcObj))
        .subscribe(resultsObject -> ...);
      

      【讨论】:

        【解决方案4】:

        不要打破链条,就像这样。

        Observable.from(Arrays.asList(new String[] {"1", "2", "3", }))
        .map(s -> Integer.valueOf(s))
        .reduce(new ArrayList<Integer>, (list, s) -> {
            list.add(s);
            return list;
        })
        .subscribe(i -> {
            // Do some thing with 'i', it's a list of Integer.
        });
        

        【讨论】:

        • 您可以使用 toList() 代替 reduce 以获得更简洁的代码。
        • 实际上toList 存在问题,如上述评论中所述。 reduce 解决了他们stackoverflow.com/questions/35734060/…
        • 不。我的评论不属实。 :) reduce 也有同样的问题,因为 reduce 也在等待完成。
        【解决方案5】:

        使用嵌套映射函数进行非阻塞转换

        val ints: Observable<List<Int>> = Observable.fromArray(listOf(1, 2, 3))
        
        val strings: Observable<List<String>> = ints.map { list -> list.map { it.toString() } }
        

        【讨论】:

          【解决方案6】:

          您可以使用map 运算符。例如,如果您有整数列表s,并且想要转换为双精度列表s

              List<Integer> li = new ArrayList<>();
              Observable.just(li).map( l -> {
                  List<Double> lr = new ArrayList<Double>();
                  for(Integer e:l) {
                      lr.add(e.doubleValue());
                  }
                  return lr;
              });
          

          但是,如果您可以控制 observable 并将其更改为观察单个元素而不是集合,这一切都会更加自然。将单个整数元素转换为双整数元素的相同代码:

          Observable.just(1,2,3).map( elem -> elem.doubleValue())
          

          【讨论】:

          • 不会just(li) 发出单个项目(列表),而不是像from(li) 这样的列表中的每个元素?
          • @cricket_007 是的,但是根据我对 Yura 想要发出列表的问题的理解,列表中没有任何元素(它使用 onNext(List))。正如我在回复中所说,这不是更自然,但有时可以是你想要的。
          【解决方案7】:

          作为Noel 的扩展,很好的答案。 假设转换还取决于订阅期间可能更改的某些服务器数据。在这种情况下,请使用 flatMap + scan

          因此,当 id 列表更改时,转换将重新开始。并且当与特定 id 相关的服务器数据发生变化时,单个项目也将被重新转换。

          fun getFarmsWithGroves(): Observable<List<FarmWithGroves>> {
          
                  return subscribeForIds() //may change during subscription
                          .switchMap { idList: Set<String> ->
          
                              Observable.fromIterable(idList)
                                      .flatMap { id: String -> transformId(id) } //may change during subscription
                                      .scan(emptyList<FarmWithGroves>()) { collector: List<FarmWithGroves>, candidate: FarmWithGroves ->
                                          updateList(collector, candidate)
                                      }
                          }
              }
          

          【讨论】:

            【解决方案8】:

            使用 toList() 会等待源 observable 完成,因此如果您在无限的 observable 上执行此操作(例如绑定到 UI 事件和数据库调用的对象),您将永远不会在 subscribe() 中获得任何内容。

            解决方案是使用 FlatMapSingleSwitchMapSingle

            Observable<List<Note>> observable =   noteDao.getAllNotes().flatMapSingle(list -> Observable.fromIterable(list).toList());
            

            现在,每次更新列表时,您都可以使该事件表现为可观察的。

            【讨论】:

              【解决方案9】:

              如果您需要的只是List&lt;A&gt;List&lt;B&gt; 而无需处理List&lt;B&gt; 的结果。

              最干净的版本是:

              List<A> a = ... // ["1", "2", ..]
              List<B> b = Observable.from(a)
                                    .map(a -> new B(a))
                                    .toList()
                                    .toBlocking()
                                    .single();
              

              【讨论】:

                猜你喜欢
                • 2013-08-14
                • 2015-09-03
                • 1970-01-01
                • 1970-01-01
                • 2018-01-05
                • 2014-07-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多