Python-подобное понимание списка в Java - PullRequest
53 голосов
/ 22 мая 2009

Поскольку Java не разрешает передавать методы в качестве параметров, какой прием вы используете для реализации Python-подобного понимания списков в Java?

У меня есть список (ArrayList) строк. Мне нужно преобразовать каждый элемент с помощью функции, чтобы я получил другой список. У меня есть несколько функций, которые принимают строку в качестве входных данных и возвращают другую строку в качестве выходных данных. Как мне сделать универсальный метод, которому можно дать список и функцию в качестве параметров, чтобы я мог получить список обратно с каждым обработанным элементом. В буквальном смысле это невозможно, но какой трюк мне использовать?

Другой вариант - написать новую функцию для каждой меньшей функции обработки строк, которая просто циклически перебирает весь список, что не так круто.

Ответы [ 6 ]

46 голосов
/ 24 мая 2013

В Java 8 вы можете использовать ссылки на методы:

List<String> list = ...;
list.replaceAll(String::toUpperCase);

Или, если вы хотите создать новый экземпляр списка:

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
35 голосов
/ 22 мая 2009

По сути, вы создаете интерфейс Function:

public interface Func<In, Out> {
    public Out apply(In in);
}

и затем передайте анонимный подкласс вашему методу.

Ваш метод может применить функцию к каждому элементу на месте:

public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
    ListIterator<T> itr = list.listIterator();
    while (itr.hasNext()) {
        T output = f.apply(itr.next());
        itr.set(output);
    }
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
    public String apply(String in) {
        return in.toLowerCase();
    }
});

или создайте новый List (в основном, создавая отображение из списка ввода в список вывода):

public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
    List<Out> out = new ArrayList<Out>(in.size());
    for (In inObj : in) {
        out.add(f.apply(inObj));
    }
    return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
    public String apply(String in) {
        return in.toLowerCase();
    }
});

Какой из них предпочтительнее, зависит от вашего варианта использования. Если ваш список очень большой, решение на месте может быть единственным жизнеспособным; если вы хотите применить много разных функций к одному и тому же исходному списку для создания множества производных списков, вам понадобится версия map.

17 голосов
/ 22 мая 2009

Библиотека Google Collections имеет множество классов для работы с коллекциями и итераторами на гораздо более высоком уровне, чем поддерживает обычная Java, и функциональным образом (фильтр, отображение, свертывание и т. Д.). Он определяет интерфейсы и методы Function и Predicate, которые используют их для обработки коллекций, так что вам не нужно это делать. У этого также есть вспомогательные функции, которые делают работу с дженериками Java менее трудной.

Я также использую Hamcrest ** для фильтрации коллекций.

Две библиотеки легко комбинируются с классами адаптеров.


** Заявление о заинтересованности: я в соавторстве написал Hamcrest

5 голосов
/ 22 мая 2009
1 голос
/ 18 июня 2015

Я строю этот проект для написания списка в Java, теперь это доказательство концепции в https://github.com/farolfo/list-comprehension-in-java

Примеры

// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}

Predicate<Integer> even = x -> x % 2 == 0;

List<Integer> evens = new ListComprehension<Integer>()
    .suchThat(x -> {
        x.belongsTo(Arrays.asList(1, 2, 3, 4));
        x.is(even);
    });
// evens = {2,4};

И если мы хотим преобразовать выходное выражение, например,

// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}

List<Integer> duplicated = new ListComprehension<Integer>()
    .giveMeAll((Integer x) -> x * 2)
    .suchThat(x -> {
        x.belongsTo(Arrays.asList(1, 2, 3, 4));
        x.is(even);
    });
// duplicated = {4,8}
0 голосов
/ 23 августа 2016

Вы можете использовать лямбда-выражения для функции, например:

class Comprehension<T> {
    /**
    *in: List int
    *func: Function to do to each entry
    */
    public List<T> comp(List<T> in, Function<T, T> func) {
        List<T> out = new ArrayList<T>();
        for(T o: in) {
            out.add(func.apply(o));
        }
        return out;
    }
}

использование:

List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
    a.equals("a")? "1": 
            (a.equals("b")? "2": 
                (a.equals("c")? "3":
                    (a.equals("d")? "4": a
    )))
});

вернет:

["1", "2", "3", "4", "cheese"]
...