2017-01-07 4 views
1

Я использую Google Cloud Java API для получения объектов из облачного хранилища Google (GCS). Код для этого читает что-то вроде этого:Lazily recurse Java 8 stream

Storage storage = ... 
List<StorageObject> storageObjects = storage.objects().list(bucket).execute().getItems(); 

Но это не вернет все элементы (объекты хранения) в ведре ГКС, он будет возвращать только первые 1000 пунктов в первой «страницы». Поэтому для того, чтобы получить следующий 1000 пунктов нужно сделать:

Storage.Objects.List list = storage.objects().list(bucket).execute(); 
String nextPageToken = objects.getNextPageToken(); 
List<StorageObject> itemsInFirstPage = objects.getItems(); 

if (nextPageToken != null) { 
    // recurse 
} 

То, что я хочу сделать, это найти элемент, который соответствует Predicate при обходе всех элементов в ведре ГКС, пока предикат не соответствует. Чтобы сделать это эффективным, я хотел бы загружать только элементы на следующей странице, когда элемент не был найден на текущей странице. Для одной страницы это работает:

Predicate<StorageObject> matchesItem = ... 
takeWhile(storage.objects().list(bucket).execute().getItems().stream(), not(matchesItem)); 

Где takeWhile копируется из here.

И это будет загружать объекты хранения со всех страниц рекурсивно:

private Stream<StorageObject> listGcsPageItems(String bucket, String pageToken) { 
    if (pageToken == null) { 
     return Stream.empty(); 
    } 


    Storage.Objects.List list = storage.objects().list(bucket); 
    if (!pageToken.equals(FIRST_PAGE)) { 
     list.setPageToken(pageToken); 
    } 
    Objects objects = list.execute(); 
    String nextPageToken = objects.getNextPageToken(); 
    List<StorageObject> items = objects.getItems(); 
    return Stream.concat(items.stream(), listGcsPageItems(bucket, nextPageToken));  
} 

где FIRST_PAGE просто «магия» String что инструктирует метод не устанавливать конкретную страницу (которая приведет к первой странице Предметы).

Проблема с этим подходом заключается в том, что он стремится, то есть все элементы со всех страниц загружаются до применения «подходящего предиката». Я бы хотел, чтобы это было ленивым (по одной странице за раз). Как я могу это достичь?

ответ

4

Я бы выполнил заказ Iterator<StorageObject> или Supplier<StorageObject>, который сохранил бы текущий список страниц и токен следующей страницы в своем внутреннем состоянии, производя StorageObject s один за другим.

Тогда я хотел бы использовать следующий код, чтобы найти первый матч:

Optional<StorageObject> result = 
    Stream.generate(new StorageObjectSupplier(...)) 
     .filter(predicate) 
     .findFirst(); 

Поставщик будет вызываться только до тех пор, не будет найдено совпадение, то есть лениво.

Другой способ заключается в реализации поставщика постранично, т.е. class StorageObjectPageSupplier implements Supplier<List<StorageObject>> и использовать потоковый API, чтобы сгладить это:

Optional<StorageObject> result = 
    Stream.generate(new StorageObjectPageSupplier(...)) 
     .flatMap(List::stream) 
     .filter(predicate) 
     .findFirst(); 
+0

Я закончил с использованием второго подхода, работает как шарм. – Johan

Смежные вопросы