java stream find match or the last one?
up vote
20
down vote
favorite
How to find the first match or the last element in a list using java stream?
Which means if no element matches the condition,then return the last element.
eg:
OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());
What should I do to make it return 5;
java lambda java-8 java-stream
add a comment |
up vote
20
down vote
favorite
How to find the first match or the last element in a list using java stream?
Which means if no element matches the condition,then return the last element.
eg:
OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());
What should I do to make it return 5;
java lambda java-8 java-stream
add a comment |
up vote
20
down vote
favorite
up vote
20
down vote
favorite
How to find the first match or the last element in a list using java stream?
Which means if no element matches the condition,then return the last element.
eg:
OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());
What should I do to make it return 5;
java lambda java-8 java-stream
How to find the first match or the last element in a list using java stream?
Which means if no element matches the condition,then return the last element.
eg:
OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());
What should I do to make it return 5;
java lambda java-8 java-stream
java lambda java-8 java-stream
edited Dec 6 at 13:33
Nicholas K
4,86641031
4,86641031
asked Dec 6 at 8:17
金潇泽
1115
1115
add a comment |
add a comment |
5 Answers
5
active
oldest
votes
up vote
13
down vote
accepted
Given the list
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
You could just do :
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.get(list.size() - 1));
Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.
If the list is empty you could return a default value, for example -1.
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
add a comment |
up vote
7
down vote
You can use reduce()
function like that:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
1
That's brilliant. You might want to use thereducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.
– daniu
Dec 6 at 9:15
13
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
5
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods inStream
calledfindAny
andfindFirst
to do this. Besides, if the stream was parallel, this wouldn't work, whilefindFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...
– Federico Peralta Schaffner
Dec 6 at 12:36
4
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
|
show 4 more comments
up vote
5
down vote
Basically I would use one of the following two methods or deviations thereof:
Stream variant:
<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
return list.stream()
.filter(filter)
.findFirst()
.orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
non-stream variant:
<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
T relevant = defaultValue;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return relevant;
}
Or as also Ilmari Karonen suggested in the comment with Iterable<T>
you are then able to even call stream::iterator
in case you really deal with a Stream
instead of a List
. Calling the shown methods would look as follows:
getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
I wouldn't use reduce
here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse
... (but that's probably just my opinion)
I probably would then even end up with something as follows:
<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
T relevant = null;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
so that calls would rather look like:
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
2
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept anyIterable<T>
instead of just aList<T>
. That way, if you do in fact need to process a stream, you can just passstream::iterator
to this method.
– Ilmari Karonen
Dec 6 at 19:03
add a comment |
up vote
3
down vote
if you want to do this in one pipeline then you could do:
int startInc = 1;
int endEx = 5;
OptionalInt first =
IntStream.concat(IntStream.range(startInc, endEx)
.filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
.findFirst();
but you're probably better off collecting the generated numbers into a list then operate on it as follows:
// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
// then operate on it
int value = result.stream()
.filter(x -> x == 7)
.findFirst()
.orElse(result.get(result.size() - 1));
Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
Optional<Integer> first =
Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
Stream.empty() : Stream.of(result.get(result.size() - 1)))
.findFirst();
add a comment |
up vote
2
down vote
I am not sure why you really want to use streams for that, a simple for
-loop would be enough:
public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
// handle empty case
if(source.isEmpty()){
return null;
}
for(T t : source){
if(predicate.test(t)){
return t;
}
}
return source.get(source.size() -1);
}
Which then can be called like:
Integer match = getFirstMatchingOrLast(ints, i -> i == 7);
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53647174%2fjava-stream-find-match-or-the-last-one%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
13
down vote
accepted
Given the list
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
You could just do :
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.get(list.size() - 1));
Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.
If the list is empty you could return a default value, for example -1.
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
add a comment |
up vote
13
down vote
accepted
Given the list
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
You could just do :
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.get(list.size() - 1));
Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.
If the list is empty you could return a default value, for example -1.
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
add a comment |
up vote
13
down vote
accepted
up vote
13
down vote
accepted
Given the list
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
You could just do :
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.get(list.size() - 1));
Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.
If the list is empty you could return a default value, for example -1.
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
Given the list
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
You could just do :
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.get(list.size() - 1));
Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.
If the list is empty you could return a default value, for example -1.
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
edited Dec 6 at 14:06
John Kugelman
239k51397451
239k51397451
answered Dec 6 at 8:58
Nicholas K
4,86641031
4,86641031
add a comment |
add a comment |
up vote
7
down vote
You can use reduce()
function like that:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
1
That's brilliant. You might want to use thereducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.
– daniu
Dec 6 at 9:15
13
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
5
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods inStream
calledfindAny
andfindFirst
to do this. Besides, if the stream was parallel, this wouldn't work, whilefindFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...
– Federico Peralta Schaffner
Dec 6 at 12:36
4
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
|
show 4 more comments
up vote
7
down vote
You can use reduce()
function like that:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
1
That's brilliant. You might want to use thereducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.
– daniu
Dec 6 at 9:15
13
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
5
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods inStream
calledfindAny
andfindFirst
to do this. Besides, if the stream was parallel, this wouldn't work, whilefindFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...
– Federico Peralta Schaffner
Dec 6 at 12:36
4
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
|
show 4 more comments
up vote
7
down vote
up vote
7
down vote
You can use reduce()
function like that:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
You can use reduce()
function like that:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
answered Dec 6 at 9:01
statut
643412
643412
1
That's brilliant. You might want to use thereducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.
– daniu
Dec 6 at 9:15
13
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
5
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods inStream
calledfindAny
andfindFirst
to do this. Besides, if the stream was parallel, this wouldn't work, whilefindFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...
– Federico Peralta Schaffner
Dec 6 at 12:36
4
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
|
show 4 more comments
1
That's brilliant. You might want to use thereducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.
– daniu
Dec 6 at 9:15
13
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
5
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods inStream
calledfindAny
andfindFirst
to do this. Besides, if the stream was parallel, this wouldn't work, whilefindFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...
– Federico Peralta Schaffner
Dec 6 at 12:36
4
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
1
1
That's brilliant. You might want to use the
reducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.– daniu
Dec 6 at 9:15
That's brilliant. You might want to use the
reducing ()
collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.– daniu
Dec 6 at 9:15
13
13
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.
– Eugene
Dec 6 at 9:39
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
@Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
– statut
Dec 6 at 9:44
5
5
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream
called findAny
and findFirst
to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...– Federico Peralta Schaffner
Dec 6 at 12:36
reduce
is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream
called findAny
and findFirst
to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst
would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...– Federico Peralta Schaffner
Dec 6 at 12:36
4
4
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
@FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.
– Holger
Dec 6 at 13:43
|
show 4 more comments
up vote
5
down vote
Basically I would use one of the following two methods or deviations thereof:
Stream variant:
<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
return list.stream()
.filter(filter)
.findFirst()
.orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
non-stream variant:
<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
T relevant = defaultValue;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return relevant;
}
Or as also Ilmari Karonen suggested in the comment with Iterable<T>
you are then able to even call stream::iterator
in case you really deal with a Stream
instead of a List
. Calling the shown methods would look as follows:
getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
I wouldn't use reduce
here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse
... (but that's probably just my opinion)
I probably would then even end up with something as follows:
<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
T relevant = null;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
so that calls would rather look like:
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
2
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept anyIterable<T>
instead of just aList<T>
. That way, if you do in fact need to process a stream, you can just passstream::iterator
to this method.
– Ilmari Karonen
Dec 6 at 19:03
add a comment |
up vote
5
down vote
Basically I would use one of the following two methods or deviations thereof:
Stream variant:
<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
return list.stream()
.filter(filter)
.findFirst()
.orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
non-stream variant:
<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
T relevant = defaultValue;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return relevant;
}
Or as also Ilmari Karonen suggested in the comment with Iterable<T>
you are then able to even call stream::iterator
in case you really deal with a Stream
instead of a List
. Calling the shown methods would look as follows:
getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
I wouldn't use reduce
here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse
... (but that's probably just my opinion)
I probably would then even end up with something as follows:
<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
T relevant = null;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
so that calls would rather look like:
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
2
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept anyIterable<T>
instead of just aList<T>
. That way, if you do in fact need to process a stream, you can just passstream::iterator
to this method.
– Ilmari Karonen
Dec 6 at 19:03
add a comment |
up vote
5
down vote
up vote
5
down vote
Basically I would use one of the following two methods or deviations thereof:
Stream variant:
<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
return list.stream()
.filter(filter)
.findFirst()
.orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
non-stream variant:
<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
T relevant = defaultValue;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return relevant;
}
Or as also Ilmari Karonen suggested in the comment with Iterable<T>
you are then able to even call stream::iterator
in case you really deal with a Stream
instead of a List
. Calling the shown methods would look as follows:
getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
I wouldn't use reduce
here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse
... (but that's probably just my opinion)
I probably would then even end up with something as follows:
<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
T relevant = null;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
so that calls would rather look like:
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
Basically I would use one of the following two methods or deviations thereof:
Stream variant:
<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
return list.stream()
.filter(filter)
.findFirst()
.orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
non-stream variant:
<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
T relevant = defaultValue;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return relevant;
}
Or as also Ilmari Karonen suggested in the comment with Iterable<T>
you are then able to even call stream::iterator
in case you really deal with a Stream
instead of a List
. Calling the shown methods would look as follows:
getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
I wouldn't use reduce
here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse
... (but that's probably just my opinion)
I probably would then even end up with something as follows:
<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
T relevant = null;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
so that calls would rather look like:
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
edited Dec 10 at 15:38
answered Dec 6 at 10:31
Roland
9,22711141
9,22711141
2
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept anyIterable<T>
instead of just aList<T>
. That way, if you do in fact need to process a stream, you can just passstream::iterator
to this method.
– Ilmari Karonen
Dec 6 at 19:03
add a comment |
2
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept anyIterable<T>
instead of just aList<T>
. That way, if you do in fact need to process a stream, you can just passstream::iterator
to this method.
– Ilmari Karonen
Dec 6 at 19:03
2
2
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any
Iterable<T>
instead of just a List<T>
. That way, if you do in fact need to process a stream, you can just pass stream::iterator
to this method.– Ilmari Karonen
Dec 6 at 19:03
I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any
Iterable<T>
instead of just a List<T>
. That way, if you do in fact need to process a stream, you can just pass stream::iterator
to this method.– Ilmari Karonen
Dec 6 at 19:03
add a comment |
up vote
3
down vote
if you want to do this in one pipeline then you could do:
int startInc = 1;
int endEx = 5;
OptionalInt first =
IntStream.concat(IntStream.range(startInc, endEx)
.filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
.findFirst();
but you're probably better off collecting the generated numbers into a list then operate on it as follows:
// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
// then operate on it
int value = result.stream()
.filter(x -> x == 7)
.findFirst()
.orElse(result.get(result.size() - 1));
Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
Optional<Integer> first =
Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
Stream.empty() : Stream.of(result.get(result.size() - 1)))
.findFirst();
add a comment |
up vote
3
down vote
if you want to do this in one pipeline then you could do:
int startInc = 1;
int endEx = 5;
OptionalInt first =
IntStream.concat(IntStream.range(startInc, endEx)
.filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
.findFirst();
but you're probably better off collecting the generated numbers into a list then operate on it as follows:
// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
// then operate on it
int value = result.stream()
.filter(x -> x == 7)
.findFirst()
.orElse(result.get(result.size() - 1));
Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
Optional<Integer> first =
Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
Stream.empty() : Stream.of(result.get(result.size() - 1)))
.findFirst();
add a comment |
up vote
3
down vote
up vote
3
down vote
if you want to do this in one pipeline then you could do:
int startInc = 1;
int endEx = 5;
OptionalInt first =
IntStream.concat(IntStream.range(startInc, endEx)
.filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
.findFirst();
but you're probably better off collecting the generated numbers into a list then operate on it as follows:
// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
// then operate on it
int value = result.stream()
.filter(x -> x == 7)
.findFirst()
.orElse(result.get(result.size() - 1));
Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
Optional<Integer> first =
Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
Stream.empty() : Stream.of(result.get(result.size() - 1)))
.findFirst();
if you want to do this in one pipeline then you could do:
int startInc = 1;
int endEx = 5;
OptionalInt first =
IntStream.concat(IntStream.range(startInc, endEx)
.filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
.findFirst();
but you're probably better off collecting the generated numbers into a list then operate on it as follows:
// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
// then operate on it
int value = result.stream()
.filter(x -> x == 7)
.findFirst()
.orElse(result.get(result.size() - 1));
Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
Optional<Integer> first =
Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
Stream.empty() : Stream.of(result.get(result.size() - 1)))
.findFirst();
edited Dec 6 at 8:56
answered Dec 6 at 8:41
Aomine
36.1k62961
36.1k62961
add a comment |
add a comment |
up vote
2
down vote
I am not sure why you really want to use streams for that, a simple for
-loop would be enough:
public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
// handle empty case
if(source.isEmpty()){
return null;
}
for(T t : source){
if(predicate.test(t)){
return t;
}
}
return source.get(source.size() -1);
}
Which then can be called like:
Integer match = getFirstMatchingOrLast(ints, i -> i == 7);
add a comment |
up vote
2
down vote
I am not sure why you really want to use streams for that, a simple for
-loop would be enough:
public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
// handle empty case
if(source.isEmpty()){
return null;
}
for(T t : source){
if(predicate.test(t)){
return t;
}
}
return source.get(source.size() -1);
}
Which then can be called like:
Integer match = getFirstMatchingOrLast(ints, i -> i == 7);
add a comment |
up vote
2
down vote
up vote
2
down vote
I am not sure why you really want to use streams for that, a simple for
-loop would be enough:
public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
// handle empty case
if(source.isEmpty()){
return null;
}
for(T t : source){
if(predicate.test(t)){
return t;
}
}
return source.get(source.size() -1);
}
Which then can be called like:
Integer match = getFirstMatchingOrLast(ints, i -> i == 7);
I am not sure why you really want to use streams for that, a simple for
-loop would be enough:
public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
// handle empty case
if(source.isEmpty()){
return null;
}
for(T t : source){
if(predicate.test(t)){
return t;
}
}
return source.get(source.size() -1);
}
Which then can be called like:
Integer match = getFirstMatchingOrLast(ints, i -> i == 7);
answered Dec 6 at 10:09
Lino
6,98221936
6,98221936
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53647174%2fjava-stream-find-match-or-the-last-one%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown