延迟递归 Java 8 流
Lazily recurse Java 8 stream
我正在使用 Google 云 Java API 从 Google 云存储 (GCS) 中获取对象。其代码如下所示:
Storage storage = ...
List<StorageObject> storageObjects = storage.objects().list(bucket).execute().getItems();
但这不会 return GCS 存储桶中的所有项目(存储对象),它只会 return 前 "page" 中的前 1000 个项目。因此,为了获得接下来的 1000 件物品,您应该这样做:
Storage.Objects.List list = storage.objects().list(bucket).execute();
String nextPageToken = objects.getNextPageToken();
List<StorageObject> itemsInFirstPage = objects.getItems();
if (nextPageToken != null) {
// recurse
}
我想做的是在遍历GCS bucket中的所有item的同时找到一个匹配Predicate
的item,直到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
只是一个 "magic" String
,指示该方法不设置特定页面(这将导致第一页项目)。
这种方法的问题在于它是急切的,即在应用 "matching predicate" 之前加载所有页面的所有项目。我希望这很懒惰(一次一页)。我怎样才能做到这一点?
我将实施自定义 Iterator<StorageObject>
或 Supplier<StorageObject>
,这将使当前页面列表和下一页标记保持在其内部状态,一个接一个地生成 StorageObject
。
然后我将使用以下代码找到第一个匹配项:
Optional<StorageObject> result =
Stream.generate(new StorageObjectSupplier(...))
.filter(predicate)
.findFirst();
Supplier 只会在找到匹配项之前被调用,即 lazily。
另一种方法是按页面实现供应商,即 class StorageObjectPageSupplier implements Supplier<List<StorageObject>>
并使用流 API 将其展平:
Optional<StorageObject> result =
Stream.generate(new StorageObjectPageSupplier(...))
.flatMap(List::stream)
.filter(predicate)
.findFirst();
我正在使用 Google 云 Java API 从 Google 云存储 (GCS) 中获取对象。其代码如下所示:
Storage storage = ...
List<StorageObject> storageObjects = storage.objects().list(bucket).execute().getItems();
但这不会 return GCS 存储桶中的所有项目(存储对象),它只会 return 前 "page" 中的前 1000 个项目。因此,为了获得接下来的 1000 件物品,您应该这样做:
Storage.Objects.List list = storage.objects().list(bucket).execute();
String nextPageToken = objects.getNextPageToken();
List<StorageObject> itemsInFirstPage = objects.getItems();
if (nextPageToken != null) {
// recurse
}
我想做的是在遍历GCS bucket中的所有item的同时找到一个匹配Predicate
的item,直到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
只是一个 "magic" String
,指示该方法不设置特定页面(这将导致第一页项目)。
这种方法的问题在于它是急切的,即在应用 "matching predicate" 之前加载所有页面的所有项目。我希望这很懒惰(一次一页)。我怎样才能做到这一点?
我将实施自定义 Iterator<StorageObject>
或 Supplier<StorageObject>
,这将使当前页面列表和下一页标记保持在其内部状态,一个接一个地生成 StorageObject
。
然后我将使用以下代码找到第一个匹配项:
Optional<StorageObject> result =
Stream.generate(new StorageObjectSupplier(...))
.filter(predicate)
.findFirst();
Supplier 只会在找到匹配项之前被调用,即 lazily。
另一种方法是按页面实现供应商,即 class StorageObjectPageSupplier implements Supplier<List<StorageObject>>
并使用流 API 将其展平:
Optional<StorageObject> result =
Stream.generate(new StorageObjectPageSupplier(...))
.flatMap(List::stream)
.filter(predicate)
.findFirst();