eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

1. Overview

In this tutorial, we’ll learn how to solve a few common combinatorial problems. They are most likely not very useful in an everyday job; however, they’re interesting from the algorithmic perspective. We may find them handy for testing purposes.

Keep in mind that there are many different approaches to solve these problems. We’ve tried to make the solutions presented easy to grasp.

2. Generating Permutations

First, let’s start with permutations. A permutation is an act of rearranging a sequence in such a way that it has a different order.

As we know from math, for a sequence of n elements, there are n! different permutations. n! is known as a factorial operation:

n! = 1 * 2 * … * n

So, for example, for a sequence [1, 2, 3] there are six permutations:

[1, 2, 3]

[1, 3, 2]

[2, 1, 3]

[2, 3, 1]

[3, 1, 2]

[3, 2, 1]

Factorial grows very fast — for a sequence of 10 elements, we have 3,628,800 different permutations! In this case, we talk about permuting sequences, where every single element is different.

2.1. Algorithm

It’s a good idea to think about generating permutations in a recursive manner. Let’s introduce the idea of the state. It will consist of two things: the current permutation and the index of the currently processed element.

The only work to do in such a state is to swap the element with every remaining one and perform a transition to a state with the modified sequence and the index increased by one.

Let’s illustrate with an example.

We want to generate all permutations for a sequence of four elements – [1, 2, 3, 4]. So, there will be 24 permutations. The illustration below presents the partial steps of the algorithm:

permutations

 

 

Each node of the tree can be understood as a state. The red digits across the top indicate the index of the currently processed element. The green digits in the nodes illustrate swaps.

So, we start in the state [1, 2, 3, 4] with an index equal to zero. We swap the first element with each element – including the first, which swaps nothing – and move on to the next state.

Now, our desired permutations are located in the last column on the right.

2.2. Java Implementation

The algorithm written in Java is short:

private static void permutationsInternal(List<Integer> sequence, List<List<Integer>> results, int index) {
    if (index == sequence.size() - 1) {
        permutations.add(new ArrayList<>(sequence));
    }

    for (int i = index; i < sequence.size(); i++) {
        swap(sequence, i, index);
        permutationsInternal(sequence, permutations, index + 1);
        swap(sequence, i, index);
    }
}

Our function takes three parameters: the currently processed sequence, results (permutations), and the index of the element currently being processed.

The first thing to do is to check if we’ve reached the last element. If so, we add the sequence to the results list.

Then, in the for-loop, we perform a swap, do a recursive call to the method, and then swap the element back.

The last part is a little performance trick – we can operate on the same sequence object all the time without having to create a new sequence for every recursive call.

It might also be a good idea to hide the first recursive call under a facade method:

public static List<List<Integer>> generatePermutations(List<Integer> sequence) {
    List<List<Integer>> permutations = new ArrayList<>();
    permutationsInternal(sequence, permutations, 0);
    return permutations;
}

Keep in mind that the algorithm shown will work only for sequences of unique elements! Applying the same algorithm for sequences with recurring elements will give us repetitions.

3. Generating the Powerset of a Set

Another popular problem is generating the powerset of a set. Let’s start with the definition:

powerset (or power set) of set S is the set of all subsets of S including the empty set and S itself

So, for example, given a set [a, b, c], the powerset contains eight subsets:

[]

[a]

[b]

[c]

[a, b]

[a, c]

[b, c]

[a, b, c]

We know from math that, for a set containing n elements, the powerset should contain 2^n subsets. This number also grows rapidly, however not as fast as factorial.

3.1. Algorithm

This time, we’ll also think recursively. Now, our state will consist of two things: the index of the element currently being processed in a set and an accumulator.

We need to make a decision with two choices in each state: whether or not to put the current element in the accumulator. When our index reaches the end of the set, we have one possible subset. In such a way, we can generate every possible subset.

3.2. Java Implementation

Our algorithm written in Java is pretty readable:

private static void powersetInternal(
  List<Character> set, List<List<Character>> powerset, List<Character> accumulator, int index) {
    if (index == set.size()) {
        results.add(new ArrayList<>(accumulator));
    } else {
        accumulator.add(set.get(index));
        powerSetInternal(set, powerset, accumulator, index + 1);
        accumulator.remove(accumulator.size() - 1);
        powerSetInternal(set, powerset, accumulator, index + 1);
    }
}

Our function takes four parameters: a set for which we want to generate subsets, the resulting powerset, the accumulator, and the index of the currently processed element.

For simplicity, we keep our sets in lists. We want to have fast access to elements specified by index, which we can achieve it with List, but not with Set.

Additionally, a single element is represented by a single letter (Character class in Java).

First, we check if the index exceeds the set size. If it does, then we put the accumulator into the result set, otherwise we:

  • put the currently considered element into the accumulator
  • make a recursive call with incremented index and extended accumulator
  • remove the last element from the accumulator, which we added previously
  • do a call again with unchanged accumulator and the incremented index

Again, we hide the implementation with a facade method:

public static List<List<Character>> generatePowerset(List<Character> sequence) {
    List<List<Character>> powerset = new ArrayList<>();
    powerSetInternal(sequence, powerset, new ArrayList<>(), 0);
    return powerset;
}

4. Generating Combinations

Now, it’s time to tackle combinations. We define it as follows:

k-combination of a set S is a subset of k distinct elements from S, where an order of items doesn’t matter

The number of k-combinations is described by the binomial coefficient:

 

binomial

So, for example, for the set [a, b, c] we have three 2-combinations:

[a, b]

[a, c]

[b, c]

Combinations have many combinatorial usages and explanations. As an example, let’s say we have a football league consisting of 16 teams. How many different matches can we see?

The answer is 16_2, which evaluates to 120.

4.1. Algorithm

Conceptually, we’ll do something similar to the previous algorithm for powersets. We’ll have a recursive function, with state consisting of the index of the currently processed element and an accumulator.

Again, we’ve got the same decision for each state: Do we add the element to the accumulator? This time, though, we have an additional restriction – our accumulator can’t have more than k elements.

It’s worth noticing that the binomial coefficient doesn’t necessarily need to be a huge number. For example:

100_2 is equal to 4,950, while

100_50 has 30 digits!

4.2. Java Implementation

For simplicity, we assume that elements in our set are integers.

Let’s take a look at the Java implementation of the algorithm:

private static void combinationsInternal(
  List<Integer> inputSet, int k, List<List<Integer>> results, ArrayList<Integer> accumulator, int index) {
  int needToAccumulate = k - accumulator.size();
  int canAcculumate = inputSet.size() - index;

  if (accumulator.size() == k) {
      results.add(new ArrayList<>(accumulator));
  } else if (needToAccumulate <= canAcculumate) {
      combinationsInternal(inputSet, k, results, accumulator, index + 1);
      accumulator.add(inputSet.get(index));
      combinationsInternal(inputSet, k, results, accumulator, index + 1);
      accumulator.remove(accumulator.size() - 1);
  }
}

This time, our function has five parameters: an input set, k parameter, a result list, an accumulator, and the index of the currently processed element.

We start by defining helper variables:

  • needToAccumulate – indicates how many more elements we need to add to our accumulator to get a proper combination
  • canAcculumate – indicates how many more elements we can add to our accumulator

Now, we check if our accumulator size is equal to k. If so, then we can put the copied array into the results list.

In another case, if we still have enough elements in the remaining part of the set, we make two separate recursive calls: with and without the currently processed element being put into the accumulator. This part is analogous to how we generated the powerset earlier.

Of course, this method could’ve been written to work a little bit faster. For example, we could declare needToAccumulate and canAcculumate variables later. However, we are focused on readability.

Again, a facade method hides the implementation:

public static List<List<Integer>> combinations(List<Integer> inputSet, int k) {
    List<List<Integer>> results = new ArrayList<>();
    combinationsInternal(inputSet, k, results, new ArrayList<>(), 0);
    return results;
}

5. Summary

In this article, we’ve discussed different combinatorial problems. Additionally, we’ve shown simple algorithms to solve them with implementations in Java. In some cases, these algorithms can help with unusual testing needs.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (All)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

eBook Jackson – NPI EA – 3 (cat = Jackson)