On this page:
9.1 Filtering on Array  Lists
9.1.1 Abstraction and Naming
9.1.2 Mutation
9.2 Combining Array  Lists
9.2.1 Interweaving
8.5

Lab 9: Working with ArrayLists and Loops

Goals: The goals of this lab is to get practice with designing methods on ArrayLists and using for-each and counted-for loops.

Submission: Submit your implementations to the following methods: filter, filterNot, customFilter, removeFailing, removePassing and customRemove). To earn full credit, your code must make a good attempt at the problems and be well-designed. These are due at the end of your lab period and you must be in lab to submit. You should work with your partner on these, but the submission is individual. You can submit solutions to interweave and customInterweave for extra credit, but they must be correct to earn credit.

9.1 Filtering on ArrayLists

Define a utilities class, and in it define a method with the signature
<T> ArrayList<T> filter(ArrayList<T> arr, Predicate<T> pred)
with Java’s definition of the Predicate<T> interface. This method should produce a new ArrayList<T> containing all the items of the given list that pass the predicate. Be sure not to mutate the input list.

9.1.1 Abstraction and Naming

Next, define filterNot, which has the same signature as filter, but keeps all of the elements filter does not.

As you can see, there is a lot of overlap between filter and filterNot. Abstract these two methods by defining a new method with the header:

<T> ArrayList<T> customFilter(ArrayList<T> arr, Predicate<T> pred, boolean keepPassing)

filter and filterNot are now essentially convenience methods that call customFilter. To anyone using the util class, their intent is clear just based on the names of filter and filterNot. As the designer of the util class, you only have to write one complex method. Whenever you want to create a method whose behavior can be configured by one or two simple boolean flags, it is a good idea to name each of these options and have one abstracted method do the heavy lifting.

9.1.2 Mutation

Now, define a method with the signature
<T> void removeFailing(ArrayList<T> arr, Predicate<T> pred)
that modifies the given list to remove everything that fails the predicate.

A boilerplate for-loop here will fail. Why?

Be sure to test this carefully.

Next, define removePassing and customRemove, analgous to filterNot and customFilter above.

9.2 Combining ArrayLists
9.2.1 Interweaving

Define a method with the following signature:
<T> ArrayList interweave(ArrayList<T> arr1, ArrayList<T> arr2)

The method should interweave the two lists in order, so the first element of the output list comes from arr1, the second from arr2, the third from arr1, etc. If one list runs out before the other, the output list should finish with just elements from the longer list.

Next, define a method with the following signature:
<T> ArrayList customInterweave(ArrayList<T> arr1, ArrayList<T> arr2, int getFrom1, int getFrom2)

This should work similarly to interweave, but instead of getting an element from arr1, then arr2, then arr1, etc., this should get (up to) getFrom1 elements from arr1, then (up to) getFrom2 elements from arr2, then (up to) getFrom1 elements from arr1, etc. For instance, given two lists ["A", "B", "C", "D", "E", "F"] and ["w", "x", "y", "z"], if we took three elements at a time from the first list, and two elements at a time from the second, we should produce ["A", "B", "C", "w", "x", "D", "E", "F", "y", "z"]. Similarly to the basic interweave, if one list runs out before the other, the list should finish with just elements from the longer list.

Then, redefine the first version of interweave as a call to this more general (and more complex) one.