90 * first element in the encounter order 91 * @return a {@code TerminalOp} implementing the find operation 92 */ 93 public static TerminalOp<Double, OptionalDouble> makeDouble(boolean mustFindFirst) { 94 return new FindOp<>(mustFindFirst, StreamShape.DOUBLE_VALUE, OptionalDouble.empty(), 95 OptionalDouble::isPresent, FindSink.OfDouble::new); 96 } 97 98 /** 99 * A short-circuiting {@code TerminalOp} that searches for an element in a 100 * stream pipeline, and terminates when it finds one. Implements both 101 * find-first (find the first element in the encounter order) and find-any 102 * (find any element, may not be the first in encounter order.) 103 * 104 * @param <T> the output type of the stream pipeline 105 * @param <O> the result type of the find operation, typically an optional 106 * type 107 */ 108 private static final class FindOp<T, O> implements TerminalOp<T, O> { 109 private final StreamShape shape; 110 final boolean mustFindFirst; 111 final O emptyValue; 112 final Predicate<O> presentPredicate; 113 final Supplier<TerminalSink<T, O>> sinkSupplier; 114 115 /** 116 * Constructs a {@code FindOp}. 117 * 118 * @param mustFindFirst if true, must find the first element in 119 * encounter order, otherwise can find any element 120 * @param shape stream shape of elements to search 121 * @param emptyValue result value corresponding to "found nothing" 122 * @param presentPredicate {@code Predicate} on result value 123 * corresponding to "found something" 124 * @param sinkSupplier supplier for a {@code TerminalSink} implementing 125 * the matching functionality 126 */ 127 FindOp(boolean mustFindFirst, 128 StreamShape shape, 129 O emptyValue, 130 Predicate<O> presentPredicate, 131 Supplier<TerminalSink<T, O>> sinkSupplier) { 132 this.mustFindFirst = mustFindFirst; 133 this.shape = shape; 134 this.emptyValue = emptyValue; 135 this.presentPredicate = presentPredicate; 136 this.sinkSupplier = sinkSupplier; 137 } 138 139 @Override 140 public int getOpFlags() { 141 return StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED); 142 } 143 144 @Override 145 public StreamShape inputShape() { 146 return shape; 147 } 148 149 @Override 150 public <S> O evaluateSequential(PipelineHelper<T> helper, 151 Spliterator<S> spliterator) { 152 O result = helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).get(); 153 return result != null ? result : emptyValue; 154 } 155 156 @Override 157 public <P_IN> O evaluateParallel(PipelineHelper<T> helper, 158 Spliterator<P_IN> spliterator) { 159 return new FindTask<>(this, helper, spliterator).invoke(); 160 } 161 } 162 163 /** 164 * Implementation of @{code TerminalSink} that implements the find 165 * functionality, requesting cancellation when something has been found 166 * 167 * @param <T> The type of input element 168 * @param <O> The result type, typically an optional type 169 */ 170 private abstract static class FindSink<T, O> implements TerminalSink<T, O> { 171 boolean hasValue; 172 T value; 173 174 FindSink() {} // Avoid creation of special accessor 175 176 @Override 177 public void accept(T value) { 178 if (!hasValue) { 179 hasValue = true; 233 accept((Double) value); 234 } 235 236 @Override 237 public OptionalDouble get() { 238 return hasValue ? OptionalDouble.of(value) : null; 239 } 240 } 241 } 242 243 /** 244 * {@code ForkJoinTask} implementing parallel short-circuiting search 245 * @param <P_IN> Input element type to the stream pipeline 246 * @param <P_OUT> Output element type from the stream pipeline 247 * @param <O> Result type from the find operation 248 */ 249 @SuppressWarnings("serial") 250 private static final class FindTask<P_IN, P_OUT, O> 251 extends AbstractShortCircuitTask<P_IN, P_OUT, O, FindTask<P_IN, P_OUT, O>> { 252 private final FindOp<P_OUT, O> op; 253 254 FindTask(FindOp<P_OUT, O> op, 255 PipelineHelper<P_OUT> helper, 256 Spliterator<P_IN> spliterator) { 257 super(helper, spliterator); 258 this.op = op; 259 } 260 261 FindTask(FindTask<P_IN, P_OUT, O> parent, Spliterator<P_IN> spliterator) { 262 super(parent, spliterator); 263 this.op = parent.op; 264 } 265 266 @Override 267 protected FindTask<P_IN, P_OUT, O> makeChild(Spliterator<P_IN> spliterator) { 268 return new FindTask<>(this, spliterator); 269 } 270 271 @Override 272 protected O getEmptyResult() { 273 return op.emptyValue; 274 } 275 276 private void foundResult(O answer) { 277 if (isLeftmostNode()) 278 shortCircuit(answer); 279 else 280 cancelLaterNodes(); 281 } 282 283 @Override 284 protected O doLeaf() { 285 O result = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).get(); 286 if (!op.mustFindFirst) { 287 if (result != null) 288 shortCircuit(result); 289 return null; 290 } 291 else { 292 if (result != null) { 293 foundResult(result); 294 return result; 295 } 296 else 297 return null; 298 } 299 } 300 301 @Override 302 public void onCompletion(CountedCompleter<?> caller) { 303 if (op.mustFindFirst) { 304 for (FindTask<P_IN, P_OUT, O> child = leftChild, p = null; child != p; 305 p = child, child = rightChild) { 306 O result = child.getLocalResult(); 307 if (result != null && op.presentPredicate.test(result)) { 308 setLocalResult(result); 309 foundResult(result); 310 break; 311 } 312 } 313 } 314 super.onCompletion(caller); 315 } 316 } 317 } 318 | 90 * first element in the encounter order 91 * @return a {@code TerminalOp} implementing the find operation 92 */ 93 public static TerminalOp<Double, OptionalDouble> makeDouble(boolean mustFindFirst) { 94 return new FindOp<>(mustFindFirst, StreamShape.DOUBLE_VALUE, OptionalDouble.empty(), 95 OptionalDouble::isPresent, FindSink.OfDouble::new); 96 } 97 98 /** 99 * A short-circuiting {@code TerminalOp} that searches for an element in a 100 * stream pipeline, and terminates when it finds one. Implements both 101 * find-first (find the first element in the encounter order) and find-any 102 * (find any element, may not be the first in encounter order.) 103 * 104 * @param <T> the output type of the stream pipeline 105 * @param <O> the result type of the find operation, typically an optional 106 * type 107 */ 108 private static final class FindOp<T, O> implements TerminalOp<T, O> { 109 private final StreamShape shape; 110 final int opFlags; 111 final O emptyValue; 112 final Predicate<O> presentPredicate; 113 final Supplier<TerminalSink<T, O>> sinkSupplier; 114 115 /** 116 * Constructs a {@code FindOp}. 117 * 118 * @param mustFindFirst if true, must find the first element in 119 * encounter order, otherwise can find any element 120 * @param shape stream shape of elements to search 121 * @param emptyValue result value corresponding to "found nothing" 122 * @param presentPredicate {@code Predicate} on result value 123 * corresponding to "found something" 124 * @param sinkSupplier supplier for a {@code TerminalSink} implementing 125 * the matching functionality 126 */ 127 FindOp(boolean mustFindFirst, 128 StreamShape shape, 129 O emptyValue, 130 Predicate<O> presentPredicate, 131 Supplier<TerminalSink<T, O>> sinkSupplier) { 132 this.opFlags = StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED); 133 this.shape = shape; 134 this.emptyValue = emptyValue; 135 this.presentPredicate = presentPredicate; 136 this.sinkSupplier = sinkSupplier; 137 } 138 139 @Override 140 public int getOpFlags() { 141 return opFlags; 142 } 143 144 @Override 145 public StreamShape inputShape() { 146 return shape; 147 } 148 149 @Override 150 public <S> O evaluateSequential(PipelineHelper<T> helper, 151 Spliterator<S> spliterator) { 152 O result = helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).get(); 153 return result != null ? result : emptyValue; 154 } 155 156 @Override 157 public <P_IN> O evaluateParallel(PipelineHelper<T> helper, 158 Spliterator<P_IN> spliterator) { 159 // This takes into account the upstream ops flags and the terminal 160 // op flags and therefore takes into account findFirst or findAny 161 boolean mustFindFirst = StreamOpFlag.ORDERED.isKnown(helper.getStreamAndOpFlags()); 162 return new FindTask<>(this, mustFindFirst, helper, spliterator).invoke(); 163 } 164 } 165 166 /** 167 * Implementation of @{code TerminalSink} that implements the find 168 * functionality, requesting cancellation when something has been found 169 * 170 * @param <T> The type of input element 171 * @param <O> The result type, typically an optional type 172 */ 173 private abstract static class FindSink<T, O> implements TerminalSink<T, O> { 174 boolean hasValue; 175 T value; 176 177 FindSink() {} // Avoid creation of special accessor 178 179 @Override 180 public void accept(T value) { 181 if (!hasValue) { 182 hasValue = true; 236 accept((Double) value); 237 } 238 239 @Override 240 public OptionalDouble get() { 241 return hasValue ? OptionalDouble.of(value) : null; 242 } 243 } 244 } 245 246 /** 247 * {@code ForkJoinTask} implementing parallel short-circuiting search 248 * @param <P_IN> Input element type to the stream pipeline 249 * @param <P_OUT> Output element type from the stream pipeline 250 * @param <O> Result type from the find operation 251 */ 252 @SuppressWarnings("serial") 253 private static final class FindTask<P_IN, P_OUT, O> 254 extends AbstractShortCircuitTask<P_IN, P_OUT, O, FindTask<P_IN, P_OUT, O>> { 255 private final FindOp<P_OUT, O> op; 256 private final boolean mustFindFirst; 257 258 FindTask(FindOp<P_OUT, O> op, 259 boolean mustFindFirst, 260 PipelineHelper<P_OUT> helper, 261 Spliterator<P_IN> spliterator) { 262 super(helper, spliterator); 263 this.mustFindFirst = mustFindFirst; 264 this.op = op; 265 } 266 267 FindTask(FindTask<P_IN, P_OUT, O> parent, Spliterator<P_IN> spliterator) { 268 super(parent, spliterator); 269 this.mustFindFirst = parent.mustFindFirst; 270 this.op = parent.op; 271 } 272 273 @Override 274 protected FindTask<P_IN, P_OUT, O> makeChild(Spliterator<P_IN> spliterator) { 275 return new FindTask<>(this, spliterator); 276 } 277 278 @Override 279 protected O getEmptyResult() { 280 return op.emptyValue; 281 } 282 283 private void foundResult(O answer) { 284 if (isLeftmostNode()) 285 shortCircuit(answer); 286 else 287 cancelLaterNodes(); 288 } 289 290 @Override 291 protected O doLeaf() { 292 O result = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).get(); 293 if (!this.mustFindFirst) { 294 if (result != null) 295 shortCircuit(result); 296 return null; 297 } 298 else { 299 if (result != null) { 300 foundResult(result); 301 return result; 302 } 303 else 304 return null; 305 } 306 } 307 308 @Override 309 public void onCompletion(CountedCompleter<?> caller) { 310 if (this.mustFindFirst) { 311 for (FindTask<P_IN, P_OUT, O> child = leftChild, p = null; child != p; 312 p = child, child = rightChild) { 313 O result = child.getLocalResult(); 314 if (result != null && op.presentPredicate.test(result)) { 315 setLocalResult(result); 316 foundResult(result); 317 break; 318 } 319 } 320 } 321 super.onCompletion(caller); 322 } 323 } 324 } 325 |