diff --git a/CHANGES.md b/CHANGES.md index 4c2eed2922..a650af40b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,8 +5,14 @@ ### Changes * Updated the minimum Android API Level validation from 10 to **21**. As with previous jsoup versions, Android - developers need to enable core library desugaring. The minimum Java version remains Java - 8. [2173](https://github.com/jhy/jsoup/pull/2173) + developers need to enable core library desugaring. The minimum Java version remains Java 8. + [2173](https://github.com/jhy/jsoup/pull/2173) + +### Improvements + +* Added `Element#selectStream(String query)` and `Element#selectStream(Evaluator )` methods, that return a `Stream` of + matching elements. Elements are evaluated and returned as they are found, and the stream can be + terminated early. [2092](https://github.com/jhy/jsoup/pull/2092) ### Bug Fixes diff --git a/src/main/java/org/jsoup/nodes/Element.java b/src/main/java/org/jsoup/nodes/Element.java index aff327b115..1a1538edd6 100644 --- a/src/main/java/org/jsoup/nodes/Element.java +++ b/src/main/java/org/jsoup/nodes/Element.java @@ -486,6 +486,39 @@ public Elements select(Evaluator evaluator) { return Selector.select(evaluator, this); } + /** + Selects elements from the given root that match the specified {@link Selector} CSS query, with this element as the + starting context, and returns them as a lazy Stream. Matched elements may include this element, or any of its + children. +
+ Unlike {@link #select(String query)}, which returns a complete list of all matching elements, this method returns a + {@link Stream} that processes elements lazily as they are needed. The stream operates in a "pull" model — elements + are fetched from the root as the stream is traversed. You can use standard {@code Stream} operations such as + {@code filter}, {@code map}, or {@code findFirst} to process elements on demand. +
+ + @param cssQuery a {@link Selector} CSS-like query + @return a {@link Stream} containing elements that match the query (empty if none match) + @throws Selector.SelectorParseException (unchecked) on an invalid CSS query. + @see Selector selector query syntax + @see QueryParser#parse(String) + @since 1.19.1 + */ + public StreamThis is effectively the same as calling {@code element.select(query).first()}, but is more efficient as query
@@ -1125,12 +1158,7 @@ public Elements getElementsByTag(String tagName) {
*/
public @Nullable Element getElementById(String id) {
Validate.notEmpty(id);
-
- Elements elements = Collector.collect(new Evaluator.Id(id), this);
- if (elements.size() > 0)
- return elements.get(0);
- else
- return null;
+ return Collector.findFirst(new Evaluator.Id(id), this);
}
/**
diff --git a/src/main/java/org/jsoup/select/Collector.java b/src/main/java/org/jsoup/select/Collector.java
index 02b0528384..4199401571 100644
--- a/src/main/java/org/jsoup/select/Collector.java
+++ b/src/main/java/org/jsoup/select/Collector.java
@@ -3,8 +3,8 @@
import org.jsoup.nodes.Element;
import org.jspecify.annotations.Nullable;
-import java.util.Optional;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Collects a list of elements that match the supplied criteria.
@@ -16,17 +16,26 @@ public class Collector {
private Collector() {}
/**
- Build a list of elements, by visiting root and every descendant of root, and testing it against the evaluator.
+ Build a list of elements, by visiting the root and every descendant of root, and testing it against the Evaluator.
@param eval Evaluator to test elements against
@param root root of tree to descend
@return list of matches; empty if none
*/
- public static Elements collect (Evaluator eval, Element root) {
- eval.reset();
+ public static Elements collect(Evaluator eval, Element root) {
+ return stream(eval, root).collect(Collectors.toCollection(Elements::new));
+ }
+
+ /**
+ Obtain a Stream of elements by visiting the root and every descendant of root and testing it against the evaluator.
- return root.stream()
- .filter(eval.asPredicate(root))
- .collect(Collectors.toCollection(Elements::new));
+ @param evaluator Evaluator to test elements against
+ @param root root of tree to descend
+ @return A {@link Stream} of matches
+ @since 1.19.1
+ */
+ public static Stream One Hello there