使用Stream.forEach()
时,我在想是否无法在流不为空时添加预执行和后期操作。例如,在打印List时,可以在流为空时添加内容或写入其他内容。
现在我想出了像
这样的东西private static <T> void forEach(Stream<T> stream, Consumer<? super T> action,
Runnable preAction, Runnable postAction, Runnable ifEmpty) {
AtomicBoolean hasElements = new AtomicBoolean(false);
Consumer<T> preActionConsumer = x -> {
if (hasElements.compareAndSet(false, true)) {
preAction.run();
}
};
stream.forEach(preActionConsumer.andThen(action));
if (hasElements.get()) {
postAction.run();
} else {
ifEmpty.run();
}
}
对于顺序流,这应该有效,如果不是的话? 这种方法是否正确,它是“好主意”有这样的方法还是有任何警告?
这对并行流不起作用,因为preAction
可能比执行action
的另一个线程慢,但正确实现它而不诉诸同步或其他并发工具,这会破坏并行的目的溪流可能不容易......
编辑:添加用例。使用正则表达式从文件中读取整数并将其写入另一个文件。使用这种方法,我不必在内存中创建一个String,然后将其写入某个文件。 (显然,对于我的实际任务,我使用的是更复杂的正则表达式。)
public static void main(String[] args) throws IOException {
Stream<String> lines = Files.lines(Paths.get("foo.txt"));
Pattern findInts = Pattern.compile("(\\d+)");
Path barFile = Paths.get("bar.txt");
try (BufferedWriter writer = Files.newBufferedWriter(barFile , StandardOpenOption.CREATE_NEW)) {
lines.flatMap(x -> findInts.matcher(x).results())
.forEach(x-> convertCheckedIOException(() -> {
writer.write(x.group(1));
writer.newLine();
})
);
}
}
public static void convertCheckedIOException(Run r) {
try {
r.run();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
interface Run {
void run() throws IOException;
}
答案 0 :(得分:2)
使用合适的工具完成工作。此任务不会从Stream API中受益。
func splitVideoUrl(videoUrl:String) -> String {
//let token = videoUrl.split(separator: "user/")
let ttstr:String = " https://www.youtube.com/user/myYoutubeChennal"
if let range = ttstr.range(of: "user/") {
let firstPart = String(describing: ttstr.startIndex..<range.lowerBound)
print(firstPart) // print Hello
return firstPart
}
return ""
}
您仍然可以引入Stream操作,例如
Pattern intPattern = Pattern.compile("\\d+");
try(Scanner scanner = new Scanner(Paths.get("foo.txt"));
BufferedWriter writer = Files.newBufferedWriter(Paths.get("bar.txt"), CREATE_NEW)) {
String s = scanner.findWithinHorizon(intPattern, 0);
if(s == null) {
// perform empty action
} else {
// perform pre action
do {
writer.append(s);
writer.newLine();
} while( (s=scanner.findWithinHorizon(intPattern, 0)) != null);
// perform post action
}
}
但是必须处理已检查的Pattern intPattern = Pattern.compile("\\d+");
try(Scanner scanner = new Scanner(Paths.get("foo.txt"));
BufferedWriter writer = Files.newBufferedWriter(Paths.get("bar.txt"), CREATE_NEW)) {
String firstLine = scanner.findWithinHorizon(intPattern, 0);
if(firstLine == null) {
// perform empty action
} else {
// perform pre action
Stream.concat(Stream.of(firstLine),
scanner.findAll(intPattern).map(MatchResult::group))
.forEach(line -> convertCheckedIOException(() -> {
writer.write(line);
writer.newLine();
})
);
// perform post action
}
}
只会使代码复杂化而无益。
答案 1 :(得分:1)
我喜欢拥有这样的工具的想法。起初我想过使用由preaction设置/取消设置的第二个标志,并且停止操作可能就足够了。但更复杂的是事先将preAction放在每个动作调用的前面,而不仅仅是第一个动作调用。
我提出了一个同步解决方案,可以强制执行订单pre
,actions
,post/empty
。需要注意的是,第一批并行线程必须等待第一批并行线程完成,因为它们会遇到synchronized
:
private static <T> void forEach(Stream<T> stream, Consumer<? super T> action, Runnable preAction, Runnable postAction, Runnable ifEmpty)
{
AtomicBoolean hasElements = new AtomicBoolean(false);
stream.forEach(new Consumer<T>()
{
private Consumer<? super T> delegate = new Consumer<T>()
{
private Consumer<? super T> delegate2 = new Consumer<T>()
{
@Override
public void accept(T x)
{
System.out.println("check");
hasElements.set(true);
preAction.run();
action.accept(x);
delegate2 = action; // rest of first batch won't run preAction anymore
delegate = action; // next batches won't even synchronize anymore
}
};
@Override
public void accept(T x)
{
synchronized (this)
{
delegate2.accept(x);
}
}
};
@Override
public void accept(T x)
{
delegate.accept(x);
}
});
if (hasElements.get()) { postAction.run(); } else { ifEmpty.run(); }
}
public static void main(String[] args)
{
Stream<Integer> s = Stream.generate(() -> 1).limit(1000).parallel();
forEach(s, i -> System.out.println(Thread.currentThread().getId()), () -> System.out.println("pre"),
() -> System.out.println("post"), () -> System.out.println("empty"));
}
Output:
check
pre
...
many thread IDs
...
post