Java 8 在不破坏流链的情况下将函数应用于 Stream 的所有元素
Java 8 apply function to all elements of Stream without breaking stream chain
在 Java 中有没有办法在不破坏 Stream
链的情况下将函数应用于 Stream
的所有元素?我知道我可以调用 forEach
,但是那个方法 returns 是 void
,而不是 Stream
。
您正在查找 Stream
的 map()
函数。
示例:
List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
我认为您正在寻找 Stream.peek
。但是请仔细阅读文档,因为它主要是作为一种调试方法设计的。来自文档:
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
传递给 peek
的操作必须是 non interfering。
不完全确定 breaking the stream chain
是什么意思,但是对 return 和 Stream
的 Stream
的任何操作都不会 break 或 使用 您的流。流由 terminal operations
消耗,正如您所指出的 forEach
不会 return 和 Stream<T>
因此结束流,通过在 forEach 和 forEach 本身之前执行所有 intermediate
操作。
在您在评论中提供的示例中:
myStream.map(obj -> {obj.foo(); return obj;}
你真的不能用一张衬垫做到这一点。当然你可以使用方法引用,但是你的 returned Stream
将是不同的类型(假设 foo
returns 是一种类型):
myStream.map(Obj::foo) // this will turn into Stream<T>, where T is
// the return type of foo, instead of Stream<Obj>
此外,您的 map
操作是 stateful
,强烈建议不要这样做。您的代码可以编译,甚至可以按您希望的方式工作——但稍后可能会失败。 map
操作应该是 stateless
.
有(至少)3种方法。为了示例代码,我假设您想调用 2 个消费者方法 methodA
和 methodB
:
一个。使用 peek()
:
list.stream().peek(x -> methodA(x)).forEach(x -> methodB(x));
尽管文档说它只用于 "debug",但它确实有效(并且现在正在生产中)
乙。使用 map()
调用 methodA,然后 return 将元素返回流:
list.stream().map(x -> {method1(x); return x;}).forEach(x -> methodB(x));
这可能是最 "acceptable" 的方法。
摄氏度。在 forEach()
中做两件事:
list.stream().forEach(x -> {method1(x); methodB(x);});
这是最不灵活的,可能不适合您的需要。
您最好的选择是将地图应用到您的流中。其中 returns 一个流,由将给定函数应用于流元素的结果组成。
例如:
IntStream.range(1, 100)
.boxed()
.map(item->item+3)
.map(item->item*2)...
我们正在对流进行多项修改,但在某些情况下我们不想修改流。我们只想访问每个元素,然后将其不加修改地传递到流中(就像流中的 peek() 方法 API)。在这种情况下,我们可以
StreamItem peekyMethod(StreamItem streamItemX) {
// .... visit the streamItemX
//Then pass it down the stream
return streamItemX;
}
我认为最干净的方法是向流中的对象添加一个修改器。
例如,
class Victim {
private String tag;
private Victim withTag(String t)
this.tag = t;
return this;
}
}
List<Victim> base = List.of(new Victim());
Stream<Victim> transformed = base.stream().map(v -> v.withTag("myTag"));
如果您愿意(很多人愿意),您可以使用 withTag 方法创建并 return 一个新的 Victim;这使您可以使 Victim 不可变。
您可以使用 map
方法,但您必须创建 returns this
的辅助方法。例如:
public class Fluent {
public static <T> Function<T, T> of(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
}
当你想调用void
方法时使用它:
list.stream().map(Fluent.of(SomeClass::method));
或者如果您想将它与带有某些参数的方法一起使用:
list.stream().map(Fluent.of(x -> x.method("hello")))
在 Java 中有没有办法在不破坏 Stream
链的情况下将函数应用于 Stream
的所有元素?我知道我可以调用 forEach
,但是那个方法 returns 是 void
,而不是 Stream
。
您正在查找 Stream
的 map()
函数。
示例:
List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
我认为您正在寻找 Stream.peek
。但是请仔细阅读文档,因为它主要是作为一种调试方法设计的。来自文档:
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
传递给 peek
的操作必须是 non interfering。
不完全确定 breaking the stream chain
是什么意思,但是对 return 和 Stream
的 Stream
的任何操作都不会 break 或 使用 您的流。流由 terminal operations
消耗,正如您所指出的 forEach
不会 return 和 Stream<T>
因此结束流,通过在 forEach 和 forEach 本身之前执行所有 intermediate
操作。
在您在评论中提供的示例中:
myStream.map(obj -> {obj.foo(); return obj;}
你真的不能用一张衬垫做到这一点。当然你可以使用方法引用,但是你的 returned Stream
将是不同的类型(假设 foo
returns 是一种类型):
myStream.map(Obj::foo) // this will turn into Stream<T>, where T is
// the return type of foo, instead of Stream<Obj>
此外,您的 map
操作是 stateful
,强烈建议不要这样做。您的代码可以编译,甚至可以按您希望的方式工作——但稍后可能会失败。 map
操作应该是 stateless
.
有(至少)3种方法。为了示例代码,我假设您想调用 2 个消费者方法 methodA
和 methodB
:
一个。使用 peek()
:
list.stream().peek(x -> methodA(x)).forEach(x -> methodB(x));
尽管文档说它只用于 "debug",但它确实有效(并且现在正在生产中)
乙。使用 map()
调用 methodA,然后 return 将元素返回流:
list.stream().map(x -> {method1(x); return x;}).forEach(x -> methodB(x));
这可能是最 "acceptable" 的方法。
摄氏度。在 forEach()
中做两件事:
list.stream().forEach(x -> {method1(x); methodB(x);});
这是最不灵活的,可能不适合您的需要。
您最好的选择是将地图应用到您的流中。其中 returns 一个流,由将给定函数应用于流元素的结果组成。 例如:
IntStream.range(1, 100)
.boxed()
.map(item->item+3)
.map(item->item*2)...
我们正在对流进行多项修改,但在某些情况下我们不想修改流。我们只想访问每个元素,然后将其不加修改地传递到流中(就像流中的 peek() 方法 API)。在这种情况下,我们可以
StreamItem peekyMethod(StreamItem streamItemX) {
// .... visit the streamItemX
//Then pass it down the stream
return streamItemX;
}
我认为最干净的方法是向流中的对象添加一个修改器。
例如,
class Victim {
private String tag;
private Victim withTag(String t)
this.tag = t;
return this;
}
}
List<Victim> base = List.of(new Victim());
Stream<Victim> transformed = base.stream().map(v -> v.withTag("myTag"));
如果您愿意(很多人愿意),您可以使用 withTag 方法创建并 return 一个新的 Victim;这使您可以使 Victim 不可变。
您可以使用 map
方法,但您必须创建 returns this
的辅助方法。例如:
public class Fluent {
public static <T> Function<T, T> of(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
}
当你想调用void
方法时使用它:
list.stream().map(Fluent.of(SomeClass::method));
或者如果您想将它与带有某些参数的方法一起使用:
list.stream().map(Fluent.of(x -> x.method("hello")))