2009-05-22 3 views
47

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

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

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

+2

как fyi, вы можете использовать Jython или Scala для получения списков на JVM – geowa4

+3

... или Clojure! :) – Ashe

+0

SMH, читая все ответы на это. В Python вы можете легко написать понимание списка в одной строке из 40-60 символов. Все предлагаемые решения здесь представляют собой несколько строк, и большинство из них длиннее, чем одна строка, которую он будет использовать в Python. – ArtOfWarfare

ответ

33

В принципе, вы создаете интерфейс Функции:

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.

+1

Но тогда вы просите меня помещать каждую маленькую функцию в другой класс, поскольку у них должно быть стандартное имя («применимо» в вашем случае). Правильно ? – euphoria83

+1

Не обязательно; ваш анонимный класс может просто вызвать небольшую функцию внутри apply(). Это так же близко, как Java получает функции указателей, не вникая в опасения отражения. –

+0

doToList изобретает колесо. То, что вы здесь сделали, - плохой дизайн того, что обычно называют картой. Обычный интерфейс public static Список map (Список , Func f); То, что он делает, - это создать другой список, а не изменять его. Если вам нужно изменить исходный список, не разрушая ссылку, просто выполните .clear(), за которым следует addAll(). Не комбинируйте все это одним способом. – Pyrolistical

16

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

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

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


** Декларация интересов: Я написал в соавторстве Hamcrest

+11

Из любопытства, почему это называется Hamcrest? Я все еще не могу понять, звучит ли это вкусно или нет. –

+12

Это анаграмма «матчи». – Nat

5

Apache Commons CollectionsUtil.transform(Collection, Transformer) - еще один вариант.

+0

К сожалению, это не общий. В этом случае все это означает некоторое дополнительное кастинг, но это может быть проблемой в других случаях. –

27

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

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

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

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList()); 
+7

Вопрос: 7 лет, а Java 8 не существует. Это должен быть принятый ответ сейчас;) – zpontikas

1

Я строй этого проект, чтобы написать список понимание в 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} 
+1

Часть красоты понимания списка Python - это то, насколько оно коротким. Ваши 6 длинных строк Java могут быть записаны как «[x * 2 для x в 1, 2, 3, 4, если x% 2 == 0]» ... 1 строка из 41 символа. Не уверен, сколько из вашего кода просто ужасно читать из-за того, насколько чертовски многословная Java - это то, что из-за того, что ваша библиотека не делает что-то достаточно сжато. – ArtOfWarfare

+0

Здесь все еще лучше, чем многие другие решения, мне это действительно нравится – rhbvkleef

0

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

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"] 
Смежные вопросы