Подтвердить что ты не робот

RxJava - выбор каждого элемента в списке

У меня есть метод, который возвращает Observable<ArrayList<Long>>, которые являются идентификаторами некоторых элементов. Я бы хотел просмотреть этот список и загрузить каждый элемент с помощью другого метода, который возвращает Observable<Item>.

Как мне это сделать с помощью операторов RxJava?

4b9b3361

Ответ 1

Здесь небольшой самодостаточный пример

public class Example {

    public static class Item {
        int id;
    }

    public static void main(String[] args) {
        getIds()
                .flatMapIterable(ids -> ids) // Converts your list of ids into an Observable which emits every item in the list
                .flatMap(Example::getItemObservable) // Calls the method which returns a new Observable<Item>
                .subscribe(item -> System.out.println("item: " + item.id));
    }

    // Simple representation of getting your ids.
    // Replace the content of this method with yours
    private static Observable<List<Integer>> getIds() {
        return Observable.just(Arrays.<Integer>asList(1, 2, 3));
    }

    // Replace the content of this method with yours
    private static Observable<Item> getItemObservable(Integer id) {
        Item item = new Item();
        item.id = id;
        return Observable.just(item);
    }
}

Обратите внимание, что Observable.just(Arrays.<Integer>asList(1, 2, 3)) является простым представлением Observable<ArrayList<Long>> из вашего вопроса. Вы можете заменить его своим собственным наблюдаемым в своем коде.

Это должно дать вам основу того, что вам нужно.

p/s: используйте метод flatMapIterable для этого случая, так как он принадлежит Iterable, как показано ниже:

/**
 * Implementing this interface allows an object to be the target of
 * the "for-each loop" statement. See
 * <strong>
 * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides /language/foreach.html">For-each Loop</a>
 * </strong>
 *
 * @param <T> the type of elements returned by the iterator
 *
 * @since 1.5
 * @jls 14.14.2 The enhanced for statement
  */
 public interface Iterable<T>

Ответ 2

Используйте Transformer, который изменяет источник Observable, вызывая flatMap на нем с помощью функции. Вы можете думать об этом как о двухэтапном процессе:

  • Функция принимает каждый испущенный элемент (a Iterable<T>) и переизлучает его как Observable<T>
  • flatMap принимает каждый из этих испущенных объектов Observable<T>, объединяет их в один Observable<T>

Трансформатор выглядит следующим образом:

public class FlattenTransform<T> implements Observable.Transformer<Iterable<T>, T> {
    @Override
    public Observable<? extends T> call(Observable<? extends Iterable<T>> source) {
        return source.flatMap(new Func1<Iterable<T>, Observable<T>>() {
            @Override
            public Observable<T> call(Iterable<T> values) {
                return Observable.from(values);
            }
        });
    }
}

Как только вы создали свой трансформатор, вы можете использовать compose для применения преобразования в наблюдаемом источнике:

public class Example {

    private static final ArrayList<Long> sourceList = new ArrayList<>(Arrays.asList(new Long[] {1L,2L,3L}));
    private static final Observable<ArrayList<Long>> listObservable = Observable.just(sourceList);
    private static final FlattenTransform<Long> flattenList = new FlattenTransform<Long>();

    public static void main(String[] args) {
        listObservable.compose(flattenList).subscribe(printItem);
    }

    private static Action1<Long> printItem = new Action1<Long>() {
        @Override
        public void call(Long item) {
            System.out.println("item: " + item);
        }
    };
}

Преимущество использования compose с Transformer вместо flatMap с Func1 заключается в том, что если в будущем, если вам нужно снова сгладить список, вам даже не придется думать о том, какой оператор использовать (map? flatMap? concatMap?). Другими словами, операция flatmap выпекается в классе FlattenTransform и эта деталь абстрагируется.

Трансформаторы также имеют другие преимущества, такие как возможность объединения нескольких операций.

Ответ 3

В качестве альтернативы flatMapIterable вы можете сделать это с помощью flatMap:

Observable.just(Arrays.asList(1, 2, 3)) //we create an Observable that emits a single array
            .flatMap(numberList -> Observable.fromIterable(numberList)) //map the list to an Observable that emits every item as an observable
            .flatMap(number -> downloadFoo(number)) //download smth on every number in the array
            .subscribe(...);


private ObservableSource<? extends Integer> downloadFoo(Integer number) {
   //TODO
}

Лично я думаю, что .flatMap(numberList -> Observable.fromIterable(numberList)) легче читать и понимать, чем .flatMapIterable(numberList -> numberList ).

Различие похоже на порядок (RxJava2):

  • Observable.fromIterable: Преобразует итерационную последовательность в наблюдаемый источник, который испускает элементы в последовательности.
  • Observable.flatMapIterable: Возвращает Observable, который объединяет каждый элемент, испускаемый источником ObservableSource, со значениями в Iterable, соответствующими этому элементу, который генерируется селектором.

Используя ссылки на методы, это выглядит так:

Observable.just(Arrays.asList(1, 2, 3))
            .flatMap(Observable::fromIterable)
            .flatMap(this::downloadFoo)