Since Java doesn't allow passing methods as parameters, what trick do you use to implement Python like list comprehension in Java ?
由于Java不允许将方法作为参数传递,那么在Java中实现Python(如列表理解)有什么诀窍呢?
I have a list (ArrayList) of Strings. I need to transform each element by using a function so that I get another list. I have several functions which take a String as input and return another String as output. How do I make a generic method which can be given the list and the function as parameters so that I can get a list back with each element processed. It is not possible in the literal sense, but what trick should I use ?
我有一个字符串列表(ArrayList)。我需要使用一个函数来转换每个元素,这样我就可以得到另一个列表。我有几个函数,它们以一个字符串作为输入,然后返回另一个字符串作为输出。如何创建一个通用方法,该方法可以将列表和函数作为参数,以便在处理每个元素时返回一个列表。这在字面上是不可能的,但是我应该使用什么技巧呢?
The other option is to write a new function for each smaller String-processing function which simply loops over the entire list, which is kinda not so cool.
另一个选项是为每个更小的字符串处理函数编写一个新函数,它只是循环遍历整个列表,这不是很酷。
6 个解决方案
#1
33
Basically, you create a Function interface:
基本上,你创建了一个函数界面:
public interface Func<In, Out> {
public Out apply(In in);
}
and then pass in an anonymous subclass to your method.
然后将一个匿名子类传递给您的方法。
Your method could either apply the function to each element in-place:
您的方法可以将该函数应用于适当的每个元素:
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();
}
});
or create a new List
(basically creating a mapping from the input list to the output 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();
}
});
Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map
version.
哪个更好取决于您的用例。如果你的列表非常大,那么就地解决方案可能是唯一可行的方案;如果您希望对同一个原始列表应用许多不同的函数来生成许多派生列表,您将需要映射版本。
#2
34
In Java 8 you can use method references:
在Java 8中,您可以使用方法引用:
List<String> list = ...;
list.replaceAll(String::toUpperCase);
Or, if you want to create a new list instance:
或者,如果您想创建一个新的列表实例:
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
#3
16
The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.
谷歌集合库有许多类,用于在比普通Java支持高得多的级别上使用集合和迭代器,并且以功能方式(过滤器、映射、折叠等)。它定义了函数和谓词接口以及使用它们处理集合的方法,这样您就不必这么做了。它还具有方便的函数,使处理Java泛型变得不那么困难。
I also use Hamcrest** for filtering collections.
我还使用Hamcrest**来过滤集合。
The two libraries are easy to combine with adapter classes.
这两个库很容易与适配器类结合。
** Declaration of interest: I co-wrote Hamcrest
**利益声明:我与人合写了Hamcrest
#4
5
Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.
Apache Commons CollectionsUtil。变换(收集,变换)是另一个选择。
#5
1
I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java
我正在构建这个项目,用Java编写列表理解,现在是https://github.com/farolfo/list- comprehensive -in- Java概念的证明
Examples
例子
// { 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};
And if we want to transform the output expression in some way like
如果我们想要像这样变换输出表达式
// { 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}
#6
0
You can use lambdas for the function, like so:
你可以使用lambdas函数,如下所示:
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;
}
}
the usage:
的用法:
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
)))
});
will return:
将返回:
["1", "2", "3", "4", "cheese"]
#1
33
Basically, you create a Function interface:
基本上,你创建了一个函数界面:
public interface Func<In, Out> {
public Out apply(In in);
}
and then pass in an anonymous subclass to your method.
然后将一个匿名子类传递给您的方法。
Your method could either apply the function to each element in-place:
您的方法可以将该函数应用于适当的每个元素:
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();
}
});
or create a new List
(basically creating a mapping from the input list to the output 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();
}
});
Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map
version.
哪个更好取决于您的用例。如果你的列表非常大,那么就地解决方案可能是唯一可行的方案;如果您希望对同一个原始列表应用许多不同的函数来生成许多派生列表,您将需要映射版本。
#2
34
In Java 8 you can use method references:
在Java 8中,您可以使用方法引用:
List<String> list = ...;
list.replaceAll(String::toUpperCase);
Or, if you want to create a new list instance:
或者,如果您想创建一个新的列表实例:
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
#3
16
The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.
谷歌集合库有许多类,用于在比普通Java支持高得多的级别上使用集合和迭代器,并且以功能方式(过滤器、映射、折叠等)。它定义了函数和谓词接口以及使用它们处理集合的方法,这样您就不必这么做了。它还具有方便的函数,使处理Java泛型变得不那么困难。
I also use Hamcrest** for filtering collections.
我还使用Hamcrest**来过滤集合。
The two libraries are easy to combine with adapter classes.
这两个库很容易与适配器类结合。
** Declaration of interest: I co-wrote Hamcrest
**利益声明:我与人合写了Hamcrest
#4
5
Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.
Apache Commons CollectionsUtil。变换(收集,变换)是另一个选择。
#5
1
I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java
我正在构建这个项目,用Java编写列表理解,现在是https://github.com/farolfo/list- comprehensive -in- Java概念的证明
Examples
例子
// { 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};
And if we want to transform the output expression in some way like
如果我们想要像这样变换输出表达式
// { 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}
#6
0
You can use lambdas for the function, like so:
你可以使用lambdas函数,如下所示:
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;
}
}
the usage:
的用法:
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
)))
});
will return:
将返回:
["1", "2", "3", "4", "cheese"]