Here are some common mistakes when using Java Streams:
1. Do not use the termination operation
mistake: Forgot to call the termination operation (such ascollect()
、forEach()
orreduce()
), which causes the stream to not be executed.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David"); // Create a stream but no termination operation is called () .filter(name -> ("A")); // No termination operation is called here // Since the stream is not executed, nothing will be printed ("Stream operations have not been executed."); }
Solution: Always end with a termination operation to trigger the processing of the stream.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David"); // Create a stream and call a termination operation () .filter(name -> ("A")) // Intermediate operation .forEach(::println); // Terminate the operation // This will print "Alice" because the stream is executed}
2. Modify source data
mistake: Modify the source data structure when processing the stream (e.g.List
) may lead to unknown results.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David"); // Try to modify the source list during stream processing () .filter(name -> { if (("B")) { (name); // Modify the source list } return true; }) .forEach(::println); // Due to concurrent modification, the output may not be as expected ("Remaining names: " + names); }
Solution: Do not modify the source data during a stream operation, but use the stream to create a new collection.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David"); // Create a new list based on filtering results List<String> filteredNames = () .filter(name -> ("B")) // Filter out names starting with 'B' .collect(()); // Show filtered list ("Filtered names: " + filteredNames); ("Original names remain unchanged: " + names); }
3. Ignore the overhead of parallel streams
mistake: Think parallel streams always improve performance regardless of context, such as small datasets or lightweight operations.
public static void main(String[] args) { List<Integer> numbers = (1, 2, 3, 4, 5); // Small dataset // Use parallel streams on small datasets () .map(n -> { // Simulate lightweight operations (().getName() + " processing: " + n); return n * n; }) .forEach(::println); // The output may show that unnecessary threads are created for simple tasks}
Solution: Use parallel streams with caution, especially for CPU-intensive tasks with large data sets.
public static void main(String[] args) { List<Integer> numbers = (1, 1_000_000) // Big data set .boxed() .collect(()); // Use parallel streams for CPU-intensive operations on large data sets List<Integer> squareNumbers = () .map(n -> { // Simulate CPU intensive operation return n * n; }) .collect(()); // Print the first 10 results ("First 10 squared numbers: " + (0, 10)); }
4. Overuse intermediate operations
mistake: Too many intermediate operations in chain calls (such asfilter()
andmap()
) may introduce performance overhead.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David", "Eve"); // Overuse intermediate operation List<String> result = () .filter(name -> ("A")) // The first intermediate operation .filter(name -> () > 3) // The second intermediate operation .map(String::toUpperCase) // The third intermediate operation .map(name -> name + " is a name") // The fourth intermediate operation .toList(); // Terminal operation // Output result (result); }
Solution: Minimize intermediate operations in the flow pipeline and use flow fusion where possible.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David", "Eve"); // Optimize flow pipeline List<String> result = () .filter(name -> ("A") && () > 3) // Merge filters into one .map(name -> () + " is a name") // Merge map operation .toList(); // Terminal operation // Output result (result); }
5. Not processing Optional values
mistake: In usefindFirst()
orreduce()
Waiting for operation, it was not handled correctlyOptional
result.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie"); // Try to find names starting with "Z" (does not exist) String firstNameStartingWithZ = () .filter(name -> ("Z")) .findFirst() // Return an Optional .get(); // If Optional is empty, this will throw a NoSuchElementException // Output result (firstNameStartingWithZ); }
Solution: On the visitOptional
Always check whether it exists before the value ofNoSuchElementException
。
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie"); // Correctly handle Optional Optional<String> firstNameStartingWithZ = () .filter(name -> ("Z")) .findFirst(); // Return an Optional // Check whether Optional exists if (()) { (()); } else { ("No name starts with 'Z'"); } }
6. Ignore thread safety
mistake: Using shared mutable states in parallel streams can lead to race conditions and inconsistent results.
public static void main(String[] args) { List<Integer> numbers = (1, 2, 3, 4, 5); List<Integer> results = new ArrayList<>(); // Shared variable state // Use shared variable states in parallel streams ().forEach(number -> { (number * 2); // This may lead to race conditions }); // Output result ("Results: " + results); }
Solution: Avoid sharing mutable state; use thread-safe collections or local variables.
public static void main(String[] args) { List<Integer> numbers = (1, 2, 3, 4, 5); List<Integer> results = new CopyOnWriteArrayList<>(); // Thread-safe collection // Use thread-safe collections in parallel streams ().forEach(number -> { (number * 2); // Avoid race conditions }); // Output result ("Results: " + results); }
7. Confusing intermediate operations and terminating operations
mistake: It is unclear about the difference between an intermediate operation (returning a new stream) and a terminating operation (producing a result).
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David"); // Error: Try to use the intermediate operation as a termination operation // This will not compile because 'filter' returns a Stream, not a List ().filter(name -> ("A")).forEach(::println); // The termination operation is correctly used here}
Solution: Familiar with the characteristics of each operation type to avoid logical errors in the code.
public static void main(String[] args) { List<String> names = ("Alice", "Bob", "Charlie", "David"); // Use intermediate and terminate operations correctly List<String> filteredNames = () .filter(name -> ("A")) // Intermediate operation .collect(()); // Terminate the operation // Output filtered name ("Filtered Names: " + filteredNames); }
By mastering these tips and implementing these solutions, you can better use Java Streams and write cleaner, more efficient code.
This is the end of this article about 7 common errors and solutions when using Streams in Java. For more common errors in Java Streams, please search for my previous articles or continue browsing the related articles below. I hope you will support me in the future!