diff --git a/src/main/java/org/springframework/data/util/PageCollectors.java b/src/main/java/org/springframework/data/util/PageCollectors.java
new file mode 100644
index 0000000000..7cfc6ffb14
--- /dev/null
+++ b/src/main/java/org/springframework/data/util/PageCollectors.java
@@ -0,0 +1,350 @@
+package org.springframework.data.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collector.Characteristics;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Utility methods to work with {@link Stream} and {@link Page}s.
+ * It has been thought to provide some writing like that:
+ *
+ *
+ * class DatasProvider {
+ * public Page getDatasPage() {
+ * Collection items = [FILL THE ITEMS];
+ * return items.stream().map(...).filter(...).collector(PageCollectors.toSimplePage());
+ * }
+ * }
+ *
+ * or
+ *
+ * class DatasProvider {
+ * public Page getDatasPage() {
+ * Page page = repo.[ANY PAGED SEARCH];
+ * return page.stream().map(...).filter(...).collector(PageCollectors.toSimplePage());
+ * }
+ * }
+ *
+ * or
+ *
+ * class DatasProvider {
+ * public Page getDatasPage(Pageable pageable) {
+ * Collection items = [FILL THE ITEMS];
+ * return items.stream().map(...).filter(...).collector(PageCollectors.toPage(pageable));
+ * }
+ * }
+ *
+ *
+ * @author Bertrand Moreau
+ */
+public final class PageCollectors {
+
+ private static final Set characteristics = Collections.emptySet();
+
+ /**
+ * Simply put all the {@link Stream} data into a {@link Page}.Use is IF the {@link Page} is already returned
+ * by the repo BUT processed after
+ *
+ * @param p
+ * @param
+ * @return a {@link Page} containing all the {@link Stream} data and the {@link Pageable} informations.
+ */
+ public static Collector, Page> toSimplePage(final Pageable p) {
+ return new SimplePageCollectorImpl<>(p);
+ }
+
+ /**
+ * Simply put all the {@link Stream} data into a {@link Page}.
+ *
+ * @param
+ * @return a {@link Page} containing all the {@link Stream} datas
+ */
+ public static Collector, Page> toSimplePage() {
+ return new SimplePageCollectorImpl<>();
+ }
+
+ private static class SimplePageCollectorImpl implements Collector, Page> {
+
+ private Pageable p = null;
+
+ public SimplePageCollectorImpl() {}
+
+ public SimplePageCollectorImpl(final Pageable p) {
+ this.p = Objects.requireNonNull(p);
+ }
+
+ @Override
+ public Set characteristics() {
+ return characteristics;
+ }
+
+ @Override
+ public Supplier> supplier() {
+ return ArrayList::new;
+ }
+
+ @Override
+ public BiConsumer, T> accumulator() {
+ return List::add;
+ }
+
+ @Override
+ public BinaryOperator> combiner() {
+ return (left, right) -> Stream.of(left, right).flatMap(Collection::stream).collect(Collectors.toList());
+ }
+
+ @Override
+ public Function, Page> finisher() {
+ return t -> p == null ? new PageImpl<>(t) : new PageImpl<>(t, p, t.size());
+ }
+
+ }
+
+ /**
+ * Reduce the {@link Stream} as {@link Page} based on the {@link Pageable} information.
+ *
+ * @param
+ * @param p
+ * @return a {@link Page} containing a subset of the {@link Stream}
+ */
+ public static Collector, Page> toPage(final Pageable p) {
+ return new PageCollectorImpl<>(p);
+ }
+
+ private static class PageCollectorImpl implements Collector, Page> {
+
+ private final Pageable p;
+
+ public PageCollectorImpl(final Pageable p) {
+ this.p = Objects.requireNonNull(p);
+ }
+
+ @Override
+ public Set characteristics() {
+ return characteristics;
+ }
+
+ @Override
+ public Supplier> supplier() {
+ return ArrayList::new;
+ }
+
+ @Override
+ public BiConsumer, T> accumulator() {
+ return List::add;
+ }
+
+ @Override
+ public BinaryOperator> combiner() {
+ return (left, right) -> Stream.of(left, right).flatMap(Collection::stream).collect(Collectors.toList());
+ }
+
+ @Override
+ public Function, Page> finisher() {
+ return t -> {
+ final int pageNumber = p.getPageNumber();
+ final int pageSize = p.getPageSize();
+ final int fromIndex = Math.min(t.size(), pageNumber * pageSize);
+ final int toIndex = Math.min(t.size(), (pageNumber + 1) * pageSize);
+
+ return new PageImpl<>(t.subList(fromIndex, toIndex), p, t.size());
+ };
+ }
+
+ }
+
+ /**
+ * Reduce the {@link Stream} as {@link Page} based on the {@link Pageable} information.
+ *
+ * @param
+ * @param p
+ * @return a {@link Page} containing a subset of the {@link Stream} sort following the {@link Comparator}
+ */
+ public static Collector, Page> toSortedPage(final Pageable p, final Comparator c) {
+ return new SortedPageCollectorImpl<>(p, c);
+ }
+
+ private static class SortedPageCollectorImpl implements Collector, Page> {
+
+ private final Pageable p;
+ private final Comparator c;
+
+ public SortedPageCollectorImpl(final Pageable p, final Comparator c) {
+ this.p = Objects.requireNonNull(p);
+ this.c = Objects.requireNonNull(c);
+ }
+
+ @Override
+ public Set characteristics() {
+ return characteristics;
+ }
+
+ @Override
+ public Supplier> supplier() {
+ return ArrayList::new;
+ }
+
+ @Override
+ public BiConsumer, T> accumulator() {
+ return List::add;
+ }
+
+ @Override
+ public BinaryOperator> combiner() {
+ return (left, right) -> Stream.of(left, right).flatMap(Collection::stream).collect(Collectors.toList());
+ }
+
+ @Override
+ public Function, Page> finisher() {
+ return t -> {
+ final int pageNumber = p.getPageNumber();
+ final int pageSize = p.getPageSize();
+ final int fromIndex = Math.min(t.size(), pageNumber * pageSize);
+ final int toIndex = Math.min(t.size(), (pageNumber + 1) * pageSize);
+
+ final List data = t.subList(fromIndex, toIndex);
+ data.sort(c);
+
+ return new PageImpl<>(data, p, t.size());
+ };
+ }
+
+ }
+
+ /**
+ * Reduce the {@link Stream} as {@link Page} based on the {@link Pageable} information.
+ * The {@link Stream} is filtered before subset of data isolated
+ *
+ * @param
+ * @param p
+ * @return a {@link Page} containing a subset of the {@link Stream}
+ */
+ public static Collector, Page> toFilteredPage(final Pageable p, final Predicate f) {
+ return new FilteredPageCollectorImpl<>(p, f);
+ }
+
+ private static class FilteredPageCollectorImpl implements Collector, Page> {
+
+ private final Pageable p;
+ private final Predicate f;
+
+ public FilteredPageCollectorImpl(final Pageable p, final Predicate f) {
+ this.p = Objects.requireNonNull(p);
+ this.f = Objects.requireNonNull(f);
+ }
+
+ @Override
+ public Set characteristics() {
+ return characteristics;
+ }
+
+ @Override
+ public Supplier> supplier() {
+ return ArrayList::new;
+ }
+
+ @Override
+ public BiConsumer, T> accumulator() {
+ return List::add;
+ }
+
+ @Override
+ public BinaryOperator> combiner() {
+ return (left, right) -> Stream.of(left, right).flatMap(Collection::stream).collect(Collectors.toList());
+ }
+
+ @Override
+ public Function, Page> finisher() {
+ return t -> {
+ final List data = t.stream().filter(f).collect(Collectors.toList());
+
+ final int pageNumber = p.getPageNumber();
+ final int pageSize = p.getPageSize();
+ final int fromIndex = Math.min(data.size(), pageNumber * pageSize);
+ final int toIndex = Math.min(data.size(), (pageNumber + 1) * pageSize);
+
+ return new PageImpl<>(data.subList(fromIndex, toIndex), p, t.size());
+ };
+ }
+
+ }
+
+ /**
+ * Reduce the {@link Stream} as {@link Page} based on the {@link Pageable} information.
+ * The {@link Stream} is filtered then sorted then the subset of data isolated
+ *
+ * @param
+ * @param p
+ * @return a {@link Page} containing a subset of the {@link Stream}
+ */
+ public static Collector, Page> toFilteredSortedPage(final Pageable p, final Predicate f,
+ final Comparator c) {
+ return new FilteredSortedPageCollectorImpl<>(p, f, c);
+ }
+
+ private static class FilteredSortedPageCollectorImpl implements Collector, Page> {
+
+ private final Pageable p;
+ private final Predicate f;
+ private final Comparator c;
+
+ public FilteredSortedPageCollectorImpl(final Pageable p, final Predicate f, final Comparator c) {
+ this.p = Objects.requireNonNull(p);
+ this.f = Objects.requireNonNull(f);
+ this.c = Objects.requireNonNull(c);
+ }
+
+ @Override
+ public Set characteristics() {
+ return characteristics;
+ }
+
+ @Override
+ public Supplier> supplier() {
+ return ArrayList::new;
+ }
+
+ @Override
+ public BiConsumer, T> accumulator() {
+ return List::add;
+ }
+
+ @Override
+ public BinaryOperator> combiner() {
+ return (left, right) -> Stream.of(left, right).flatMap(Collection::stream).collect(Collectors.toList());
+ }
+
+ @Override
+ public Function, Page> finisher() {
+ return t -> {
+ final List data = t.stream().filter(f).sorted(c).collect(Collectors.toList());
+
+ final int pageNumber = p.getPageNumber();
+ final int pageSize = p.getPageSize();
+ final int fromIndex = Math.min(data.size(), pageNumber * pageSize);
+ final int toIndex = Math.min(data.size(), (pageNumber + 1) * pageSize);
+
+ return new PageImpl<>(data.subList(fromIndex, toIndex), p, t.size());
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/springframework/data/util/PageCollectorToSimplePageTest.java b/src/test/java/org/springframework/data/util/PageCollectorToSimplePageTest.java
new file mode 100644
index 0000000000..76d0ad6b0a
--- /dev/null
+++ b/src/test/java/org/springframework/data/util/PageCollectorToSimplePageTest.java
@@ -0,0 +1,77 @@
+package org.springframework.data.util;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Test methods for {@link PageCollectors}.
+ *
+ * @author Bertrand Moreau
+ */
+public class PageCollectorToSimplePageTest {
+
+ private List ints;
+ private int size;
+
+ @BeforeEach
+ void init() {
+ final Random rand = new Random();
+ size = rand.nextInt(10000);
+ ints = IntStream.range(0, size).mapToObj(i -> rand.nextInt()).collect(Collectors.toList());
+ }
+
+ @Test
+ void fullPage() {
+ final Page page = ints.stream().collect(PageCollectors.toSimplePage());
+
+ assertEquals(size, page.getSize());
+ assertEquals(size, page.getContent().size());
+ assertEquals(0, page.getNumber());
+ }
+
+ @Test
+ void fullPageWithPageable() {
+ final Pageable p = Pageable.ofSize(size).withPage(3);
+ final Page page = ints.stream().collect(PageCollectors.toSimplePage(p));
+
+ assertEquals(size, page.getSize());
+ assertEquals(size, page.getContent().size());
+ assertEquals(3, page.getNumber());
+ }
+
+ @Test
+ void emptyPage() {
+ final Page