i--
i--

Reputation: 153

Filter a list in Java

I have a list of people with additional information - let's say book rentals. So each Rental-object in my list would include a Person-object as an attribute and some rental information. For each person there will be 1..n entries in the list.

Now I need to filter this list based on some criteria. If ONE of the entries matches a certain criteria, I want to delete ALL entries for that person, even if the other entries do not match the criteria.

Is there a nice way to do this in one filter? Alternative would be to scan the list, identify people whose entries should be removed and then apply something like

Collections2.filter(myList, new MyPredicate(peopleIWantToRemove))

but I would like to do it with just one-time-list-traversal. How can I do this?

Upvotes: 0

Views: 990

Answers (5)

Kent
Kent

Reputation: 195049

Now I need to filter this list based on some criteria. If ONE of the entries matches a certain criteria, I want to delete ALL entries for that person, even if the other entries do not match the criteria.

I think you were not doing filtering. because filtering return a subCollection (filtered) against on some condition. You are gonna always get the same Person List but with some element changed.

You want something like python's map(list, function). going through the list, for each person do something.

Guava's collections.transform can do it.

public static <F,T> Collection<T> transform(Collection<F> fromCollection,
                            Function<? super F,T> function)

check it out:

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Collections2.html#transform(java.util.Collection, com.google.common.base.Function)

Upvotes: 0

BlairHippo
BlairHippo

Reputation: 9658

Going through the list twice (once to determine all the "bad" people, a second time to remove them) is, from an algorithm run-time standpoint, perfectly harmless. Whether you go through the list once or twice, the algorithm runs in O(n) time.

However, if you're really serious about only going through the list once, you could construct a temporary data structure that keeps track of all the places each person appears alongside a list of the people you want to get rid of.

Map<Person, List<Rental>> rentalsPerPerson;
List<People> badPeople;

When you parse the list the first time, you populate these two structures. Then you go through the list of badPeople, pull out their list of Rental objects, and purge those one at a time from your original list.

But honestly, that feels like a lot of bother for not much gain.

The way I'd recommend doing it? Go through the list twice. First pass: Compile a Set of bad people. Second pass: Create a new output List, initially empty. Go through your original List. For each element, if the Person isn't in the Bad set, add the entry to your output List.

Upvotes: 1

Ste
Ste

Reputation: 141

Sort list based on bad people criteria (bad entry first), and then filter using stateful predicate as C-Otto said

Upvotes: 0

Ryan Stewart
Ryan Stewart

Reputation: 128829

Use Iterables.filter() instead. Per the class docs: "Unless otherwise noted, all of the iterables produced in this class are lazy, which means that their iterators only advance the backing iteration when absolutely necessary." That means you can compose multiple filters, but the traversal only happens once, when you do it yourself.

Upvotes: 0

C-Otto
C-Otto

Reputation: 5843

Make the predicate stateful so that it not only matches if some criterion is met, but also if the person is known as a "bad" person.

Upvotes: 0

Related Questions