use*_*001 5 java boilerplate java-8 java-stream
我想在List的每个元素上调用lambda函数,并将结果放在一个新的List中.
我目前的版本是这样的:
b = a.stream().map(i-> i+1).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
但它似乎相当冗长,并且通过从列表到流的转换所需的所有样板代码隐藏了代码的意图,反之亦然.其他语言(例如ruby)中的相同代码将更加简洁,如下所示:
b = a.map{|i| i+1}
Run Code Online (Sandbox Code Playgroud)
当然可以做这样的事情:
for (int i: a) {
b.add(i+1);
}
Run Code Online (Sandbox Code Playgroud)
但它看起来有点过时,而且不如lambda版本灵活.
为了改变现有的List,Holger的评论 list.replaceAll(i->i+1)很好,但我有兴趣创建一个新的List对象.
是否有一种方法可以在列表的每个元素上调用lambda并创建一个带有结果的新元素?
为了更简洁,您可以使用Stream API的某个包装器,或者完全绕过它.你可以实现类似的东西:
class ListUtil {
public static <A, B> List<B> map(List<A> list, Function<? super A, ? extends B> f) {
List<B> newList = new ArrayList<>(list.size());
for(A a:list) {
newList.add(f.apply(a));
}
return newList;
}
}
Run Code Online (Sandbox Code Playgroud)
然后使用静态导入,您可以编写:
b = map(a, i -> i + 1);
Run Code Online (Sandbox Code Playgroud)
我会忽略与其他语言的比较,因为1.我对它们不熟悉,而且2.我认为理解你所要求的语言哲学可能更有帮助.
请记住,列表和流是根本不同的东西.列表(和其他集合)是内存中包含有限数量元素的实体.它包含对所有这些元素的引用.
相反,流是一个(可能是无限的)值序列,可以在运行中生成和消耗.在任何给定时间都不一定引用所有元素(甚至任何元素).
因此,为列表实现"映射"不同于为流实现"映射".对于流,概念是"在生成元素时,将函数应用于它们,并生成函数的结果".这使用API直接支持Stream.map().
对于列表,等效的概念是"给我一个列表,其元素i是将函数应用于原始列表的元素i的结果".API不支持此功能,但实现起来非常简单:
import java.util.AbstractList;
import java.util.List;
import java.util.function.Function;
/**
* Provides a view of a source list with a function applied to each element.
* This list is unmodifiable.
*
* @param <S> The type of the elements in the source list.
* @param <T> The type of the elements in the mapped list.
*/
public class MappedList<S, T> extends AbstractList<T> {
private final List<S> source ;
private final Function<S,T> map ;
public MappedList(List<S> source, Function<S,T> map) {
this.source = source ;
this.map = map ;
}
@Override
public T get(int index) {
return map.apply(source.get(index));
}
@Override
public int size() {
return source.size();
}
}
Run Code Online (Sandbox Code Playgroud)
您将使用如下:
List<Integer> original = ... ;
List<Integer> originalPlusOne = new MappedList<>(original, i -> i+1);
Run Code Online (Sandbox Code Playgroud)
如果希望代码更简洁,可以添加静态工厂方法MappedList:
public static <S,T> MappedList<S,T> mapOf(List<S> source, Function<S,T> map) {
return new MappedList<S,T>(source, map);
}
Run Code Online (Sandbox Code Playgroud)
然后就是
List<Integer> originalPlusOne = MappedList.mapOf(original, i -> i+1);
Run Code Online (Sandbox Code Playgroud)
要么
List<Integer> originalPlusOne = mapOf(original, i -> i+1);
Run Code Online (Sandbox Code Playgroud)
如果你使用静态导入.
请注意,映射列表是不可修改的,因此
originalPlusOne.add(42);
Run Code Online (Sandbox Code Playgroud)
将抛出一个异常(这是合适的,因为如果成功,将违背这个主意originalPlusOne包含的所有元素original与1加入到他们); 并注意它是原始列表的视图,因此添加元素original将添加元素originalPlusOne.你当然可以这样做
List<Integer> originalPlusOne = new ArrayList<>(new MappedList<>(original, i -> i+1));
Run Code Online (Sandbox Code Playgroud)
生成一个独立于原始列表的可修改列表.