Как добавить карту и фильтр, когда я расширяю ArrayList в Java?

99
11

Я пытаюсь создать класс table, который расширяет ArrayList. В нем я хотел бы иметь возможность создать метод map, который принимает лямбда-выражение и возвращает новый table с отображаемыми значениями. Я также хотел бы сделать это с помощью filter. Я использую карту и фильтрую много, и мне не нравится печатать все это снова и снова.


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?*/);
}
}


Я все еще пытаюсь выяснить дженерики. Может быть, есть лучший способ. Я не знаю. Я попытался дублировать то, что находится в исходном коде для ArrayList, но все, что я пытаюсь, кажется, создает новые проблемы.

спросил(а) 2018-03-17T21:53:00+03:00 1 год, 11 месяцев назад
1
Решение
104

С одной стороны, это вполне возможно:


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() может быть реализован, но он должен будет вернуть определенный подтип Table.


С другой стороны, может быть лучше иметь список как поле Table вместо его расширения: предпочитайте композицию над наследованием. Вы действительно хотите, чтобы все доступные методы ArrayList?

ответил(а) 2018-03-17T22:11:00+03:00 1 год, 11 месяцев назад
71

Просто используйте Function<E, E> для mapper, так как ваш тип возврата - это тот же самый объект и Predicate<E> для предиката как E должен быть задан.
Для таблицы любого класса реализации у вас есть, что вы будете использовать внутри collect. См. Способ сбора работ.


В основном он принимает инициализатор (first argument), BiFunction, чтобы указать, как добавить элемент в коллекцию (second argument) и, наконец, BiFunction, чтобы указать, как он работает в случае параллельного потока (third argument). См. Код ниже: -

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
}


Не стесняйтесь извлекать реализацию collect на Collector, чтобы избежать дублирования кода.

ответил(а) 2018-03-17T22:16:00+03:00 1 год, 11 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема