How to add map and filter when I extend ArrayList in Java?

I am trying to create a table class that extends an ArrayList . In it, I would like to be able to create a map method that takes a lambda expression and returns a new table with the displayed values. I would also like to do this with filter . I use a map and filter a lot, and I don’t like typing it all over and over.

 public abstract class Table<E extends Element> extends ArrayList<E> { // a lot of other stuff. public Table<E> map(/*WHAT DO I PUT HERE?*/ mapper) { return this.stream().map(mapper).collect(/*WHAT DO I PUT HERE?*/); } public Table<E> filter(/*WHAT DO I PUT HERE?*/ predicate) { return this.stream().filter(predicate).collect(/*WHAT DO I PUT HERE?*/); } } 

I'm still trying to figure out generics. Maybe there is a better way. I dont know. I tried duplicating what is in the source code for the ArrayList , but everything I try seems to create new problems.

+7
java arraylist generics java-8 extends
source share
2 answers

On the one hand, this is entirely possible:

 public abstract class Table<E extends Element> extends ArrayList<E> { // implement in concrete subclasses public abstract <E1 extends Element> Collector<E1, ?, Table<E1>> collector(); // Collector<E, ?, Table<E>> collector() is enough if map doesn't have E1 type parameter public <E1 extends Element> Table<E1> map(Function<E, E1> mapper) { return this.stream().map(mapper).collect(collector()); } public Table<E> filter(Predicate<E> predicate) { return this.stream().filter(predicate).collect(collector()); } } public class ChildTable<E extends Element> extends Table<E> { @Override public <E1 extends Element> Collector<E1, ?, Table<E1>> collector() { return Collectors.toCollection(() -> new ChildTable<E1>()); // or simpler Collectors.toCollection(ChildTable::new); } } 

collector() can be implemented, but it will have to return a specific subtype of Table .

On the other hand, it might be better to have a list as a Table field instead of expanding it: prefer composition over inheritance. Do you really want all available ArrayList methods?

+8
source share

Just use Function<E, E> for mapper, since your return type is the same object and Predicate<E> for the predicate how E should be specified. For a table of any implementation class, you have what you will use inside collect. See Method of collecting work.

It basically accepts an initializer ( first argument ), BiFunction, to indicate how to add an element to the collection ( second argument ), and finally BiFunction, to indicate how it works in the case of a parallel stream ( third argument ). See code below: -

 public abstract class Table<E extends Element> extends ArrayList<E> { public Table<E> map(Function<E, E> mapper) { return this.stream().map(mapper).collect(TableImpl::new, TableImpl::add, TableImpl::addAll); } public Table<E> filter(Predicate<E> predicate) { return this.stream().filter(predicate).collect(TableImpl::new, TableImpl::add, TableImpl::addAll); } } class TableImpl extends Table { //whatever you impl is } 

Feel free to fetch the collect implementation on Collector to avoid code duplication.

+3
source share

All Articles