Building a new list of objects that qualify.
When you need the actual objects — not just one of their fields — copy the qualifying references into a fresh list.
Filtering into a new list creates a second ArrayList holding references to objects that satisfy a condition. Unlike filtered extraction (which pulls out a field), this preserves the whole object so the caller can still call methods on it.
The result is an ArrayList of the same element type as the source. The original list is untouched; the new list shares object references with the original.
On the AP exam, this pattern appears in FRQs that ask for "a list of all students who...", "all books that...", or similar — preserving the objects for further use downstream.
Same element type in; same element type out.
Add the object itself, not a field of it.
The template
public ArrayList<SourceType> filtered(ArrayList<SourceType> source) {
ArrayList<SourceType> result = new ArrayList<SourceType>();
for (SourceType item : source) {
if (/* condition on item */) {
result.add(item);
}
}
return result;
}
Compare to filtered extraction, where the return type is different from the source. Here both lists hold the same element type — only the membership differs.
Concrete example
Assume a Student class with getGpa(). Return a new list of every student who passed (GPA at least 3.0):
public ArrayList<Student> passingStudents(ArrayList<Student> students) {
ArrayList<Student> passing = new ArrayList<Student>();
for (Student s : students) {
if (s.getGpa() >= 3.0) {
passing.add(s);
}
}
return passing;
}
Shared references
The new list holds references to the same Student objects that live in the original list. Calling a mutator through either list updates the same object visible from both.
passing.get(0).setGpa(0); // also changes students.get(?) if same object
This usually isn't a problem — it's what you'd expect — but be aware of it when the prompt cares about isolation.
Original list is unchanged
The source list still contains every original element in its original positions. Filtering is read-only on the source; only the new list is built.
Filtering vocabulary.
Used in any "select all that match" question.
- Filter — keep elements that satisfy a condition.
- Predicate — the boolean condition driving the filter.
- Result list — the new
ArrayListthat collects matching objects. - Source list — the input
ArrayList, traversed read-only. - Shared reference — both lists holding the same underlying object.
- Same element type — source and result share the generic parameter.
How object filtering is tested.
Return type and what gets added are the main check points.
What FRQ graders check:
- Return type matches source element type.
ArrayList<Student>, notArrayList<String>. - Adding the whole object.
result.add(s), notresult.add(s.getName()). - Filter direction. The condition matches the prompt exactly — "passed" vs "failed", "above" vs "at least".
- Source untouched. Don't call
removeorseton the input list.
MCQ patterns to expect: tracing which objects end up in the result; identifying whether the original list changed.
Build a list of active members.
Filter on an accessor's boolean result.
Setup. A Member class has isActive() returning boolean. Given ArrayList<Member> club, return a new list of just the active members.
public ArrayList<Member> activeMembers(ArrayList<Member> club) {
ArrayList<Member> active = new ArrayList<Member>();
for (Member m : club) {
if (m.isActive()) {
active.add(m);
}
}
return active;
}
Trace. If club contains five members with active flags [true, false, true, true, false], the result contains the 1st, 3rd, and 4th members in that order. Size is 3.
Verify isolation. The original club still has all five members, including the inactive ones. Only the new list was built.
Caller can still mutate. Because both lists share references, calling active.get(0).setActive(false) would also change club.get(0) — same object underneath.
Why this matters. Filtering into a new list is the standard "give me the subset" answer. Don't try to clone the objects unless the prompt specifically requires copies.
Filtering errors that lose marks.
Most come from confusing this with extraction or modifying the source.
- Wrong return type. Confusing this with extraction and using
ArrayList<String>instead ofArrayList<Student>. - Adding a field instead of the object.
result.add(s.getName())when you should adds. - Modifying the source list. Removing or replacing inside the source — keep it read-only.
- Inverted filter. Returning the failing students when the prompt wants the passing ones.
- Returning
nullon empty. Return the emptyArrayListinstead. - Using
setto "add".setreplaces an existing index; for growth, useadd.
Filtering vs extraction at a glance.
Two related patterns with one critical difference.
Object Filtering Reference
AP Quick Reference| Aspect | Filter into new list | Filtered extraction |
|---|---|---|
| Return type | Same as source | Type of extracted field |
| What is added | The whole object | Just one field |
| Shared references | Yes | No (primitives copied) |
| Source modified? | No | No |
| Empty result | Empty list, not null | Empty list, not null |
How to lock in object filtering.
Read the return type before writing any code.
Use the three practice tools below this lesson in order:
- MCQ Practice — for each option, check (1) is the return type the same as the source, (2) is the whole object being added, (3) is the source unchanged.
- Java Lab — write filter methods for several specs:
activeMembers,availableBooks,pendingOrders. Always preserve the source. - FRQ Practice — when the prompt says "return a list of all objects that...", use this template. Match the generic type exactly.
If you can distinguish filtering from extraction within five seconds of reading a prompt, you've mastered the choice that drives every collection FRQ.
Practice — attempt these now.
AP-style assessments aligned to this lesson. Time them.