Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In Java, making a copy of a List can sometimes produce an IndexOutOfBoundsException: “Source does not fit in dest”. In this short tutorial, we're going to look at why we get this error when using the Collections.copy method and how it can be solved. We'll also look at alternatives to Collections.copy to make a copy of the list.

2. Reproducing the Problem

Let's start with a method to create a copy of a List using the Collections.copy method:

static List<Integer> copyList(List<Integer> source) {
    List<Integer> destination = new ArrayList<>(source.size());
    Collections.copy(destination, source);
    return destination;
}

Here, the copyList method creates a new list with an initial capacity equal to the size of the source list. Then it tries to copy the elements of the source list to the destination list:

List<Integer> source = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> copy = copyList(source);

However, once we make a call to the copyList method, it throws an exception java.lang.IndexOutOfBoundsException: Source does not fit in dest.

3. Cause of the Exception

Let's try to understand what went wrong. According to the documentation for the Collections.copy method:

The destination list must be at least as long as the source list. If it's longer, the remaining elements in the destination list are unaffected.

In our example, we've created a new List using a constructor with an initial capacity equal to the size of the source list. It simply allocates enough memory and doesn't actually define elements. The size of the new list remains zero because the capacity and the size are different attributes of the List.

Therefore, when the Collections.copy method tries to copy the source list into the destination list, it throws java.lang.IndexOutOfBoundsException.

4. Solutions

4.1. Collections.copy

Let's look at a working example to copy a List to another List, using the Collections.copy method:

List<Integer> destination = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> source = Arrays.asList(11, 22, 33);
Collections.copy(destination, source);

In this case, we're copying all three elements of the source list to the destination list. The Arrays.asList method initializes the list with elements and not just a size, therefore, we're able to copy the source list to the destination list successfully.

If we just swap the arguments of the Collections.copy method, it will throw java.lang.IndexOutOfBoundsException because the size of the source list is less than the size of the destination list.

After this copy operation, the destination list looks like:

[11, 22, 33, 4, 5]

Along with the Collections.copy method, there are other ways in Java to make a copy of List. Let's take a look at some of them.

4.2. ArrayList Constructor

The simplest approach to copy a List is using a constructor that takes a Collection parameter:

List<Integer> source = Arrays.asList(11, 22, 33);
List<Integer> destination = new ArrayList<>(source);

Here, we simply pass the source list to the constructor of the destination list, which creates a shallow copy of the source list.

The destination list will be just another reference to the same object referenced by the source list. So, every change made by any reference will affect the same object.

Therefore, using a constructor is a good option for copying immutable objects like Integers and Strings.

4.3. addAll

Another simple way is to use the addAll method of List:

List<Integer> destination = new ArrayList<>();
destination.addAll(source);

The addAll method will copy all the elements of the source list to the destination list.

There are a couple of points to note regarding this approach:

  1. It creates a shallow copy of the source list.
  2. The elements of the source list are appended to the destination list.

4.4. Java 8 Streams

Java 8 has introduced the Stream API, which is a great tool for working with Java Collections.

Using the stream() method, we make a copy of the list using Stream API:

List<Integer> copy = source.stream()
  .collect(Collectors.toList());

4.5. Java 10

Copying a List is even simpler in Java 10. Using the copyOf() method allows us to create an immutable list containing the elements of the given Collection:

List<Integer> destination = List.copyOf(sourceList);

If we want to go with this approach, we need to make sure the input List isn't null and that it doesn't contain any null elements.

5. Conclusion

In this article, we looked at how and why the Collections.copy method throws IndexOutOfBoundException “Source does not file in dest”. Along with it, we also explored different ways to copy a List to another List.

Both the pre-Java-10 examples and the Java 10 examples can be found over on GitHub.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
guest
0 Comments
Inline Feedbacks
View all comments