Stream.toList() instead of Collection.toList(): As it gives unmodifiable list if code is modifying l

Rule Proposal: Avoid Using Stream.toList() if the List is Modified After Creation

1. Description of the Rule

Why is there an issue?

The Stream.toList() method, introduced in Java 16, returns an unmodifiable list. If the returned list is modified (e.g., adding or removing elements), it results in an UnsupportedOperationException at runtime.

What could be its impact?

  • Bug Risk: Code that previously used Collectors.toList() (which returns a modifiable list) may break unexpectedly when refactored to Stream.toList().
  • Runtime Errors: Any attempt to modify the list after its creation will cause an UnsupportedOperationException, leading to unexpected crashes.
  • Maintainability Issue: Developers may unknowingly introduce immutable lists, leading to difficult-to-debug issues when modifications are later required.

What could happen in case of a successful attack?

This is not a security vulnerability, but it can cause production failures if the application logic depends on a mutable list.


2. Snippet of Noncompliant Code

java

CopyEdit

import java.util.List;
import java.util.stream.Stream;

public class Example {
    public static void main(String[] args) {
        List<String> names = Stream.of("Alice", "Bob", "Charlie").toList();
        names.add("David"); // Throws UnsupportedOperationException at runtime
    }
}

Exception Thrown:

cpp

CopyEdit

Exception in thread "main" java.lang.UnsupportedOperationException

3. Snippet of Compliant Code (Fixing the Above Noncompliant Code)

Option 1: Use Collectors.toList() for a modifiable list

java

CopyEdit

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Example {
    public static void main(String[] args) {
        List<String> names = Stream.of("Alice", "Bob", "Charlie")
                                   .collect(Collectors.toList());
        names.add("David"); // No exception
    }
}

Option 2: Wrap Stream.toList() in a new ArrayList to allow modifications

java

CopyEdit

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class Example {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(Stream.of("Alice", "Bob", "Charlie").toList());
        names.add("David"); // Works fine
    }
}

4. Exceptions to the Noncompliant Code

This rule should not raise an issue in cases where:

  • The list is not modified after creation.
  • The list is intentionally immutable for thread safety or functional programming reasons.

Example (Valid Use Case - No Modification):

java

CopyEdit

import java.util.List;
import java.util.stream.Stream;

public class Example {
    public static void main(String[] args) {
        List<String> names = Stream.of("Alice", "Bob", "Charlie").toList();
        System.out.println(names); // Valid, no modification
    }
}

Hello there, indeed this sounds like a nice rule to have!
I created a ticket to decide if we want to specify it or not.

Thanks for the suggestion!