如何从Stream中提取两个元素?例如,我试图从Stream<String>
中提取元素0和1(这些数字是任意的!)。一个天真的方法是:
List<String> strings = Arrays.asList("s0", "s1", "s2", "s3", "s4");
Consumer<String> c0 = s -> System.out.println("c0.accept(" + s + ")");
Consumer<String> c1 = s -> System.out.println("c1.accept(" + s + ")");
strings.stream().skip(0).peek(c0).skip(1).peek(c1).findAny();
这会产生以下输出:
c0.accept(s0)
c0.accept(s1)
c1.accept(s1)
我理解,这是因为s0
将进入流,遇到skip(0)
,然后peek(c0)
(给出第一行)然后skip(1)
,这将跳过这个元素,然后显然继续从流的开头的下一个元素。
我以为我可以使用这些消费者来提取字符串,但c0
会被第二个元素覆盖:
String[] extracted = new String[2];
c0 = s -> extracted[0];
c1 = s -> extracted[1];
编辑:
这些是流的特征:
答案 0 :(得分:4)
鉴于您的限制,您可以将dispatch_async(dispatch_get_main_queue())
与自定义收集器结合使用,如下所示:
//...
if checkFileExists(saveURL) { // saveURL: NSURL
dispatch_async(dispatch_get_main_queue()) {
let message = "File named `\(saveURL.lastPathComponent!)` already exists. Please import using a new name or else cancel."
let alertController = UIAlertController(title: "", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addTextFieldWithConfigurationHandler { textField -> Void in
textField.text = saveURL.lastPathComponent! // it presents a text field containing the file name which needs to be changed
}
func presentAlertController() {
self.presentViewController(alertController, animated: true) {}
}
alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
alertController.addAction(UIAlertAction(title: "Rename", style: .Default) { action -> Void in
saveURL = saveURL.URLByDeletingLastPathComponent!.URLByAppendingPathComponent((alertController.textFields?.first!.text)!)
if checkFileExists(saveURL) {
presentAlertController() // currently it is calling a function to present the UIAlertController again if the file still exists when the button is clicked
} else {
saveXML(saveURL, dataObject: self.myThing)
self.fileTableView.reloadData()
}
})
presentAlertController() // this will be the first time that this called
}
}
//...
此处limit()
是包含所需元素索引的集合(不限于2)。用法:
public static <T, A, R> Collector<T, ?, R> collectByIndex(Set<Integer> wantedIndices,
Collector<T, A, R> downstream) {
class Acc {
int pos;
A acc = downstream.supplier().get();
}
return Collector.of(Acc::new, (acc, t) -> {
if(wantedIndices.contains(acc.pos++))
downstream.accumulator().accept(acc.acc, t);
}, (a, b) -> {throw new UnsupportedOperationException();}, // combining not supported
acc -> downstream.finisher().apply(acc.acc));
}
答案 1 :(得分:2)
这是我在其他答案中看不到的方法。它使用了无处不在的Pair
类的变体:
class Pair<T> {
final T first;
final T last;
Pair(T t1, T t2) { first = t1; last = t2; }
Pair(T t) { first = last = t; }
Pair<T> merge(Pair<T> other) { return new Pair<>(this.first, other.last); }
}
完成此操作后,您可以轻松获取流的第一个和最后一个元素。给定无限流和所需索引,您可以使用skip()
和limit()
修剪流以仅包含所需元素:
static <T> Pair<T> firstAndLast(Stream<T> stream, int firstIndex, int lastIndex) {
// ensure indexes >= 0 and firstIndex <= lastIndex
return stream.skip(firstIndex)
.limit(lastIndex - firstIndex + 1)
.map(Pair::new)
.reduce(Pair::merge)
.orElseThrow(() -> new IllegalArgumentException("nonexistent"));
}
其他变体包括将构造或合并逻辑内联到流操作中,而不是在Pair
类上。重构品味。
您可以这样使用它:
Stream<String> str = Stream.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
Pair<String> pair = firstAndLast(str, 4, 5);
System.out.println(pair.first + " " + pair.last);
e f
答案 2 :(得分:1)
此解决方案来自Federico Peralta Schaffner的评论:
public String[] collect(Stream<String> stream, int... positions) {
String[] collect = new String[positions.length];
Iterator<String> iterator = stream.iterator();
int skipped = 0;
for (int pos = 0; pos < positions.length; pos++) {
while (skipped++ < positions[pos]) {
iterator.next();
}
collect[pos] = iterator.next();
}
return collect;
}
这是最直接,最直接的想法,效果很好。
答案 3 :(得分:0)
您可以写下类似内容:
public static void main(String[] args) throws Exception {
List<String> strings = Arrays.asList("s0", "s1", "s2", "s3", "s4");
System.out.println(getNthElement(strings.stream(), 0)); // prints "s0"
System.out.println(getNthElement(strings.stream(), 1)); // prints "s1"
}
private static <T> T getNthElement(Stream<T> stream, int n) {
return stream.skip(n).findFirst().get();
}
请注意,如果流中的元素少于n
,则会抛出异常。此外,仅当Stream不是并行时才有意义。