Pagination is useful when dealing with large datasets, allowing us to process or display data in chunks rather than all at once. In Java, we can implement pagination using subLists, streams, or custom logic.
1. Using subList()
(Simple & Efficient)
Java’s List.subList()
method allows us to retrieve a portion of a list by specifying a start and end index.
Example: Paginating a List
import java.util.ArrayList;
import java.util.List;
public class PaginationExample {
public static <T> List<T> getPage(List<T> list, int page, int pageSize) {
int fromIndex = (page - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, list.size());
if (fromIndex >= list.size() || fromIndex < 0) {
return new ArrayList<>(); // Return an empty list if the page is out of bounds
}
return list.subList(fromIndex, toIndex);
}
public static void main(String[] args) {
List<String> items = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
items.add("Item " + i);
}
int page = 2;
int pageSize = 5;
List<String> paginatedList = getPage(items, page, pageSize);
System.out.println("Page " + page + ": " + paginatedList);
}
}
Output (Page 2, 5 items per page):
Page 2: [Item 6, Item 7, Item 8, Item 9, Item 10]
✅ Pros: Simple, efficient, and uses built-in methods.
❌ Cons: Not suitable for huge datasets (e.g., millions of records in a database).
2. Using Streams (For Functional Approach)
If you prefer Java Streams, you can use skip()
and limit()
.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class StreamPagination {
public static <T> List<T> getPage(List<T> list, int page, int pageSize) {
return list.stream()
.skip((long) (page - 1) * pageSize)
.limit(pageSize)
.collect(Collectors.toList());
}
public static void main(String[] args) {
List<Integer> numbers = IntStream.rangeClosed(1, 20)
.boxed()
.collect(Collectors.toList());
int page = 3;
int pageSize = 4;
List<Integer> paginatedList = getPage(numbers, page, pageSize);
System.out.println("Page " + page + ": " + paginatedList);
}
}
Output (Page 3, 4 items per page):
Page 3: [9, 10, 11, 12]
✅ Pros: Works well with large streams of data.
❌ Cons: Slightly more complex than subList()
.
3. Using a Custom Pagination Class
For better reusability, we can create a pagination utility class.
import java.util.List;
public class Paginator<T> {
private final List<T> items;
private final int pageSize;
public Paginator(List<T> items, int pageSize) {
this.items = items;
this.pageSize = pageSize;
}
public List<T> getPage(int page) {
int fromIndex = (page - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, items.size());
if (fromIndex >= items.size() || fromIndex < 0) {
return List.of();
}
return items.subList(fromIndex, toIndex);
}
public int getTotalPages() {
return (int) Math.ceil((double) items.size() / pageSize);
}
public boolean hasNextPage(int currentPage) {
return currentPage < getTotalPages();
}
public boolean hasPreviousPage(int currentPage) {
return currentPage > 1;
}
}
Usage Example
import java.util.List;
import java.util.stream.IntStream;
public class PaginationTest {
public static void main(String[] args) {
List<Integer> numbers = IntStream.rangeClosed(1, 20)
.boxed()
.toList();
Paginator<Integer> paginator = new Paginator<>(numbers, 5);
int currentPage = 2;
System.out.println("Page " + currentPage + ": " + paginator.getPage(currentPage));
System.out.println("Total Pages: " + paginator.getTotalPages());
System.out.println("Has Next Page: " + paginator.hasNextPage(currentPage));
System.out.println("Has Previous Page: " + paginator.hasPreviousPage(currentPage));
}
}
Output:
Page 2: [6, 7, 8, 9, 10]
Total Pages: 4
Has Next Page: true
Has Previous Page: true
✅ Pros: Reusable, supports pagination metadata.
❌ Cons: Slightly more verbose.
Which Approach Should You Use?
Approach | Pros | Cons | Best Use Case |
---|---|---|---|
subList() | Simple and efficient | Not ideal for very large lists | Small to medium-sized lists |
Streams | Functional and elegant | Slightly complex syntax | When working with data streams |
Custom Class | Reusable and extendable | More code needed | When pagination logic needs to be reused |
Conclusion
Java provides multiple ways to implement pagination, each suited for different use cases. If you’re working with a simple list, subList()
is the easiest. If you prefer a functional approach, use Streams. For a scalable and reusable solution, create a Paginator class.