Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

Java Top

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

> CHECK OUT THE COURSE

1. Introduction

In this quick tutorial, we'll learn how to generate random numbers with no duplicates using core Java classes. First, we'll implement a couple of solutions from scratch, then take advantage of Java 8+ features for a more extensible approach.

2. Random Numbers From a Small Range

If the range of numbers we need is small, we can keep adding sequential numbers to a list until we reach size n. Then, we call Collections.shuffle(), which has linear time complexity. After that, we'll end up with a randomized list of unique numbers. Let's create a utility class to generate and use those numbers:

public class UniqueRng implements Iterator<Integer> {
    private List<Integer> numbers = new ArrayList<>();

    public UniqueRng(int n) {
        for (int i = 1; i <= n; i++) {
            numbers.add(i);
        }

        Collections.shuffle(numbers);
    }
}

After constructing our object, we'll have numbers from one to size in random order. Notice we're implementing Iterator, so we'll get a random number every time we call next(). Also, we can check if we have numbers left with hasNext(). So, let's override them:

@Override
public Integer next() {
    if (!hasNext()) {
        throw new NoSuchElementException();
    }
    return numbers.remove(0);
}

@Override
public boolean hasNext() {
    return !numbers.isEmpty();
}

Consequently, remove() returns the first removed item from the list. Similarly, if we hadn't shuffled our collection, we could pass it a random index. But, shuffling at construction time has the advantage of letting us know the whole sequence in advance.

2.1. Putting It to Use

To use it, we just choose how many numbers we want and consume them:

UniqueRng rng = new UniqueRng(5);
while (rng.hasNext()) {
    System.out.print(rng.next() + " ");
}

This could result in output like:

4 1 2 5 3

3. Random Numbers From a Big Range

We need a different strategy if we want a more extensive range of numbers, only using a few of them. First, we cannot rely on adding random numbers to an ArrayList because that could generate duplicates. So, we'll use a Set because it guarantees unique items. Then, we'll use the LinkedHashSet implementation because it maintains insertion order.

This time, we'll add elements to our set in a loop until we reach size. Also, we'll use Random to generate random integers from zero to max:

public class BigUniqueRng implements Iterator<Integer> {
    private Random random = new Random();
    private Set<Integer> generated = new LinkedHashSet<>();

    public BigUniqueRng(int size, int max) {
        while (generated.size() < size) {
            Integer next = random.nextInt(max);
            generated.add(next);
        }
    }
}

Note we don't need to check if a number already exists in our set because add() does this. Now, since we can't remove items by index, we need the help of an Iterator to implement next():

public Integer next() {
    Iterator<Integer> iterator = generated.iterator();
    Integer next = iterator.next();
    iterator.remove();
    return next;
}

4. Taking Advantage of Java 8+ Features

While custom implementations are more reusable, we can create a solution using only Streams. Starting with Java 8, Random has an ints() method that returns an IntStream. We can stream it and impose the same requisites from earlier, like a range and a limit. Let's combine these features and collect the results into a Set:

Set<Integer> set = new Random().ints(-5, 15)
  .distinct()
  .limit(5)
  .boxed()
  .collect(Collectors.toSet());

The traversed set could yield output like:

-5 13 9 -4 14

With ints(), it's even simpler to have a range starting from a negative integer. But, we must be careful not to end up with an infinite stream, which would happen if we didn't call limit(), for example.

5. Conclusion

In this article, we wrote a couple of solutions to generate random numbers with no duplicates in two scenarios. First, we've made those classes iterable so we could easily consume them. Then, we created a more organic solution using streams.

And as always, the source code is available over on GitHub.

Java bottom

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

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!