How to chop a list into small chunks in Java?

2 min read

How to chop a list into small chunks in Java?

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?

ApproachProsConsBest Use Case
subList()Simple and efficientNot ideal for very large listsSmall to medium-sized lists
StreamsFunctional and elegantSlightly complex syntaxWhen working with data streams
Custom ClassReusable and extendableMore code neededWhen 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.

🤞 Never miss a story from us, get weekly updates to your inbox!