In this tutorial, I will demonstrate how to find even and odd numbers in a List using the Java Stream API. This is a common Java coding interview question that helps you understand stream filtering, lambda expressions, and collection processing.
An even number is an integer that is divisible by 2 without leaving a remainder. An odd number is an integer that leaves a nonzero remainder when divided by 2.
Java Streams make it easy to separate even and odd numbers without writing traditional loops. You can use the filter() method to select values that match a condition or Collectors.partitioningBy() to divide a list into two groups.
In this tutorial, you will learn how to identify even and odd numbers using Streams, how to separate them using different approaches, and how to handle negative numbers, duplicates, and empty lists.
1. Problem Statement
Write a Java program to find even and odd numbers from a List of integers using the Stream API.
Example:
Input: [10, 15, 20, 25, 30, 35, 40] Output: Even Numbers: [10, 20, 30, 40] Odd Numbers: [15, 25, 35]
Explanation: Numbers divisible by 2 are classified as even, while the remaining integers are classified as odd.
The original order of elements is preserved in both resulting lists.
2. Method 1: Find Even and Odd Numbers Using Stream.filter()
The simplest way to find even and odd numbers using Java Streams is to use the filter() method.
The filter() method accepts a Predicate, which determines whether each element should be included in the result.
For even numbers, the condition is:
number % 2 == 0
For odd numbers, the condition is:
number % 2 != 0
Java Program Using Stream.filter()
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class EvenOddNumbersUsingStreams {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(
10, 15, 20, 25, 30, 35, 40
);
// Find even numbers
List<Integer> evenNumbers = numbers.stream()
.filter(number -> number % 2 == 0)
.collect(Collectors.toList());
// Find odd numbers
List<Integer> oddNumbers = numbers.stream()
.filter(number -> number % 2 != 0)
.collect(Collectors.toList());
System.out.println("Original List: " + numbers);
System.out.println("Even Numbers: " + evenNumbers);
System.out.println("Odd Numbers: " + oddNumbers);
}
}
Output
Original List: [10, 15, 20, 25, 30, 35, 40] Even Numbers: [10, 20, 30, 40] Odd Numbers: [15, 25, 35]
3. Step-by-Step Explanation
Step 1: Create a List of Integers
List<Integer> numbers = Arrays.asList(
10, 15, 20, 25, 30, 35, 40
);
This statement creates a List containing seven integers.
The list includes both even and odd numbers.
Step 2: Convert the List into a Stream
numbers.stream()
The stream() method creates a sequential stream that allows you to process the list elements using Stream API operations.
Step 3: Filter Even Numbers
.filter(number -> number % 2 == 0)
The filter() method evaluates each element using the lambda expression.
The modulus operator % calculates the remainder after division.
- If
number % 2 == 0, the number is even. - If the condition is false, the number is excluded from the even-number stream.
Step 4: Filter Odd Numbers
.filter(number -> number % 2 != 0)
This condition selects numbers whose remainder after division by 2 is not zero.
For example:
15 % 2produces1, so 15 is odd.20 % 2produces0, so 20 is even.-7 % 2produces-1, so -7 is odd.
Using != 0 is important because it correctly identifies negative odd numbers.
Step 5: Collect the Results into Lists
.collect(Collectors.toList());
The collect() operation gathers the filtered values into a List.
The original list remains unchanged.
4. Method 2: Find Even and Odd Numbers Using Collectors.partitioningBy()
The Collectors.partitioningBy() method provides another way to separate even and odd numbers.
Instead of filtering the original list twice, this method divides the elements into two groups based on a boolean condition during a single stream traversal.
The result is a Map<Boolean, List<Integer>> containing two entries:
truecontains elements that satisfy the predicate.falsecontains elements that do not satisfy the predicate.
If the predicate checks whether a number is even, the true group contains even numbers and the false group contains odd numbers.
Java Program Using partitioningBy()
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class EvenOddUsingPartitioning {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(
10, 15, 20, 25, 30, 35, 40
);
// Partition numbers into even and odd groups
Map<Boolean, List<Integer>> partitioned =
numbers.stream()
.collect(Collectors.partitioningBy(
number -> number % 2 == 0
));
List<Integer> evenNumbers = partitioned.get(true);
List<Integer> oddNumbers = partitioned.get(false);
System.out.println("Original List: " + numbers);
System.out.println("Even Numbers: " + evenNumbers);
System.out.println("Odd Numbers: " + oddNumbers);
}
}
Output
Original List: [10, 15, 20, 25, 30, 35, 40] Even Numbers: [10, 20, 30, 40] Odd Numbers: [15, 25, 35]
How partitioningBy() Works
Consider this statement:
Map<Boolean, List<Integer>> partitioned =
numbers.stream()
.collect(Collectors.partitioningBy(
number -> number % 2 == 0
));
The predicate number % 2 == 0 checks whether each integer is even.
Based on the result, the collector places each number into the appropriate group.
Conceptually, the resulting map contains:
true = [10, 20, 30, 40] false = [15, 25, 35]
Retrieve the two lists using:
List<Integer> evenNumbers = partitioned.get(true);
List<Integer> oddNumbers = partitioned.get(false);
This approach is especially useful when your program needs both groups.
5. Method 3: Find Even and Odd Numbers Using Stream.toList() in Java 16+
If you use Java 16 or later, you can simplify the filtering code by using the Stream.toList() method.
This method collects stream elements into an unmodifiable List.
Java Program
import java.util.Arrays;
import java.util.List;
public class EvenOddUsingToList {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(
10, 15, 20, 25, 30, 35, 40
);
// Even numbers
List<Integer> evenNumbers = numbers.stream()
.filter(number -> number % 2 == 0)
.toList();
// Odd numbers
List<Integer> oddNumbers = numbers.stream()
.filter(number -> number % 2 != 0)
.toList();
System.out.println("Even Numbers: " + evenNumbers);
System.out.println("Odd Numbers: " + oddNumbers);
}
}
Output
Even Numbers: [10, 20, 30, 40] Odd Numbers: [15, 25, 35]
The toList() method makes the code concise, but remember that the returned lists cannot be modified.
For Java 8 compatibility, use collect(Collectors.toList()) instead.
6. Dry Run: Separating Even and Odd Numbers
Let's understand how the filtering condition works with the following list:
Input: [10, 15, 20, 25, 30]
For each number, the algorithm calculates the remainder after division by 2.
| Number | number % 2 | Classification | Selected Group |
|---|---|---|---|
| 10 | 0 | Even | Even List |
| 15 | 1 | Odd | Odd List |
| 20 | 0 | Even | Even List |
| 25 | 1 | Odd | Odd List |
| 30 | 0 | Even | Even List |
Final result:
Even Numbers: [10, 20, 30] Odd Numbers: [15, 25]
7. Find Even and Odd Numbers Including Negative Values
Even and odd classification works with negative integers as well.
However, you should use number % 2 != 0 when checking for odd numbers instead of number % 2 == 1.
The second condition does not correctly identify negative odd numbers in Java.
Java Program
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class EvenOddWithNegativeNumbers {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(
-10, -7, -4, -3, 0, 5, 8, 11
);
Map<Boolean, List<Integer>> result =
numbers.stream()
.collect(Collectors.partitioningBy(
number -> number % 2 == 0
));
System.out.println("Even Numbers: " + result.get(true));
System.out.println("Odd Numbers: " + result.get(false));
}
}
Output
Even Numbers: [-10, -4, 0, 8] Odd Numbers: [-7, -3, 5, 11]
Notice that zero is classified as an even number because it is divisible by 2 with a remainder of zero.
8. Count Even and Odd Numbers Using Streams
Sometimes, an interview question asks you to count even and odd numbers instead of returning their values.
You can use the count() terminal operation to determine how many elements match each condition.
Java Program
import java.util.Arrays;
import java.util.List;
public class CountEvenOddNumbers {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(
10, 15, 20, 25, 30, 35, 40
);
long evenCount = numbers.stream()
.filter(number -> number % 2 == 0)
.count();
long oddCount = numbers.stream()
.filter(number -> number % 2 != 0)
.count();
System.out.println("Even Count: " + evenCount);
System.out.println("Odd Count: " + oddCount);
}
}
Output
Even Count: 4 Odd Count: 3
The count() method returns a long representing the number of elements in the filtered stream.
9. Time and Space Complexity
Let's compare the main approaches.
| Approach | Time Complexity | Result Storage | List Traversals |
|---|---|---|---|
| Two filter() operations | O(n) | O(n) | 2 |
| Collectors.partitioningBy() | O(n) | O(n) | 1 |
| Two count() operations | O(n) | O(1) | 2 |
Here, n represents the number of elements in the input list.
Filtering or partitioning requires checking each element, so the overall time complexity is O(n).
When you collect even and odd numbers into lists, the results require O(n) additional storage because every input element appears in one of the output lists.
If you only count matching elements, the counting results require constant additional space.
Which Approach Is More Efficient?
If you need only even numbers or only odd numbers, filter() is a natural choice.
If you need both lists, Collectors.partitioningBy() is generally preferable because it classifies the elements in one traversal.
10. Handling Important Edge Cases
Case 1: Empty List
Input: [] Output: Even Numbers: [] Odd Numbers: []
Both filtering and partitioning handle empty lists.
Case 2: All Numbers Are Even
Input: [2, 4, 6, 8] Output: Even Numbers: [2, 4, 6, 8] Odd Numbers: []
Case 3: All Numbers Are Odd
Input: [1, 3, 5, 7] Output: Even Numbers: [] Odd Numbers: [1, 3, 5, 7]
Case 4: List Contains Duplicate Values
Input: [2, 3, 2, 4, 3, 5] Output: Even Numbers: [2, 2, 4] Odd Numbers: [3, 3, 5]
Duplicate values are preserved. Filtering does not remove duplicates unless you also apply distinct().
Case 5: List Contains Zero
Input: [0, 1, 2, 3] Output: Even Numbers: [0, 2] Odd Numbers: [1, 3]
Zero is an even number.
Case 6: List Contains Null Elements
The examples assume that every list element is a non-null Integer.
If null values are possible, filter them out before applying the modulus operation:
List<Integer> evenNumbers = numbers.stream()
.filter(number -> number != null)
.filter(number -> number % 2 == 0)
.toList();
This prevents a NullPointerException caused by unboxing a null Integer.
11. Common Mistakes to Avoid
Mistake 1: Using number % 2 == 1 for Odd Numbers
A common mistake is checking odd numbers using:
number % 2 == 1
This condition fails for negative odd numbers because Java can produce a negative remainder.
Use the following condition instead:
number % 2 != 0
Mistake 2: Assuming filter() Modifies the Original List
The filter() method produces a stream containing matching elements. It does not remove elements from the original collection.
Use a terminal operation such as collect(), toList(), or count() to obtain the result.
Mistake 3: Reusing the Same Stream
A Stream cannot be reused after a terminal operation has consumed it.
For example, you cannot call collect() twice on the same stream instance.
Create a new stream for each filtering operation, or use partitioningBy() to produce both groups in a single pipeline.
Mistake 4: Confusing partitioningBy() Keys
When you use number % 2 == 0 as the predicate:
truerepresents even numbers.falserepresents odd numbers.
If you change the predicate, the meanings of the keys may change accordingly.
12. Frequently Asked Questions
Q1. How do I find even numbers using Java Streams?
Use the filter() method with the condition number % 2 == 0.
List<Integer> even = numbers.stream()
.filter(number -> number % 2 == 0)
.toList();
Q2. How do I find odd numbers using Java Streams?
Use number % 2 != 0 as the filtering condition.
List<Integer> odd = numbers.stream()
.filter(number -> number % 2 != 0)
.toList();
Q3. How can I separate even and odd numbers in a single Stream?
Use Collectors.partitioningBy() with the condition number % 2 == 0. It divides the elements into two groups during one traversal.
Q4. What is the difference between filter() and partitioningBy()?
The filter() method selects elements that satisfy a condition. The partitioningBy() collector separates all elements into two groups based on whether the condition is true or false.
Q5. Does Java Stream filtering preserve the original order?
Yes. For a sequential ordered stream created from a List, filtering and collecting preserve the encounter order of the selected elements.
Q6. Can I find even and odd numbers from negative integers?
Yes. The modulus operator works with negative numbers. Use % 2 == 0 for even numbers and % 2 != 0 for odd numbers.
Q7. What is the time complexity of finding even and odd numbers using Streams?
The overall time complexity is O(n), where n is the number of elements.
Q8. Is zero an even or odd number?
Zero is an even number because dividing it by 2 produces a remainder of zero.
13. Conclusion
In this tutorial, you learned how to find even and odd numbers from a List using the Java Stream API.
- Stream.filter(): Selects even or odd numbers based on a condition.
- Collectors.partitioningBy(): Separates even and odd numbers into two groups in one traversal.
- Stream.toList(): Provides a concise way to collect results in Java 16 and later.
- Stream.count(): Counts matching even or odd numbers.
For Java coding interviews, I recommend learning the filter() approach first because it is simple and clearly demonstrates stream filtering with lambda expressions.
Once you understand filtering, practice Collectors.partitioningBy(). It is especially useful when you need to divide a collection into two groups based on a boolean condition.
These techniques are also helpful for more advanced Java Stream problems involving filtering, grouping, partitioning, and counting collection elements.
14. Official Java Documentation
For more information about the Java APIs used in this tutorial, refer to the official Oracle documentation:
- Java Stream Interface — Documentation for
filter(),count(), andtoList(). - Java Collectors Class — Includes the
partitioningBy()andtoList()collectors. - Java Predicate Interface — Explains boolean-valued functions used by
filter(). - Java List Interface — Documentation for ordered lists and their operations.
Continue Learning with Java Guides
Want to take your programming skills to the next level? Explore my practical Udemy courses on Java, Spring Boot, Microservices, Full-Stack Development, Docker, and Generative AI. You can also follow my structured learning roadmaps to become a professional developer.
By Ramesh Fadatare - Udemy Instructor Partner
Comments
Post a Comment
Leave Comment