You can skip the first match by adding skip(1) after the filter:
employees.stream()
.filter(e -> e.getName().charAt(0) == 's')
.skip(1)
.findAny()
.ifPresent(e -> System.out.println("Employee : " + e));
Answer from Eran on Stack OverflowIf you're on Java8 you can use a Stream:
RECOBeacon second = recoBeacons.stream().skip(1).findFirst().orElse(null);
The nice thing about this solution is that findFirst returns an Optional, so you don't have to do the hasNext checks like when using an iterator.
Also note that the Collection interface does not guarantee order, so getting the n-th element may yield unexpected results.
You must use the same iterator to fetch both the first and the second elements:
Iterator<RECOBeacon> iter = recoBeacons.iterator();
RECOBeacon first = iter.next();
RECOBeacon second = iter.next()
It would be better to call iter.hasNext() before each call to iter.next(), to avoid an exception when the Collection has less than two elements.
It is possible to get the last element with the method Stream::reduce. The following listing contains a minimal example for the general case:
Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);
This implementations works for all ordered streams (including streams created from Lists). For unordered streams it is for obvious reasons unspecified which element will be returned.
The implementation works for both sequential and parallel streams. That might be surprising at first glance, and unfortunately the documentation doesn't state it explicitly. However, it is an important feature of streams, and I try to clarify it:
- The Javadoc for the method Stream::reduce states, that it "is not constrained to execute sequentially".
- The Javadoc also requires that the "accumulator function must be an associative, non-interfering, stateless function for combining two values", which is obviously the case for the lambda expression
(first, second) -> second. - The Javadoc for reduction operations states: "The streams classes have multiple forms of general reduction operations, called reduce() and collect() [..]" and "a properly constructed reduce operation is inherently parallelizable, so long as the function(s) used to process the elements are associative and stateless."
The documentation for the closely related Collectors is even more explicit: "To ensure that sequential and parallel executions produce equivalent results, the collector functions must satisfy an identity and an associativity constraints."
Back to the original question: The following code stores a reference to the last element in the variable last and throws an exception if the stream is empty. The complexity is linear in the length of the stream.
CArea last = data.careas
.stream()
.filter(c -> c.bbox.orientationHorizontal)
.reduce((first, second) -> second).get();
If you have a Collection (or more general an Iterable) you can use Google Guava's
Iterables.getLast(myIterable)
as handy oneliner.
One of the prime motivations for the introduction of Java streams was to allow parallel operations. This led to a requirement that operations on Java streams such as map and filter be independent of the position of the item in the stream or the items around it. This has the advantage of making it easy to split streams for parallel processing. It has the disadvantage of making certain operations more complex.
So the simple answer is that there is no easy way to do things such as take every nth item or map each item to the sum of all previous items.
The most straightforward way to implement your requirement is to use the index of the list you are streaming from:
List<String> list = ...;
return IntStream.range(0, list.size())
.filter(n -> n % 3 == 0)
.mapToObj(list::get)
.toList();
A more complicated solution would be to create a custom collector that collects every nth item into a list.
class EveryNth<C> {
private final int nth;
private final List<List<C>> lists = new ArrayList<>();
private int next = 0;
private EveryNth(int nth) {
this.nth = nth;
IntStream.range(0, nth).forEach(i -> lists.add(new ArrayList<>()));
}
private void accept(C item) {
lists.get(next++ % nth).add(item);
}
private EveryNth<C> combine(EveryNth<C> other) {
other.lists.forEach(l -> lists.get(next++ % nth).addAll(l));
next += other.next;
return this;
}
private List<C> getResult() {
return lists.get(0);
}
public static Collector<Integer, ?, List<Integer>> collector(int nth) {
return Collector.of(() -> new EveryNth(nth),
EveryNth::accept, EveryNth::combine, EveryNth::getResult));
}
This could be used as follows:
Stream.of("Anne", "Bill", "Chris", "Dean", "Eve", "Fred", "George")
.parallel().collect(EveryNth.collector(3)).toList();
Which returns the result ["Anne", "Dean", "George"] as you would expect.
This is a very inefficient algorithm even with parallel processing. It splits all items it accepts into n lists and then just returns the first. Unfortunately it has to keep all items through the accumulation process because it's not until they are combined that it knows which list is the nth one.
Given the complexity and inefficiency of the collector solution I would definitely recommend sticking with the indices based solution above in preference to this if you can. If you aren't using a collection that supports get (e.g. you are passed a Stream rather than a List) then you will either need to collect the stream using Collectors.toList or use the EveryNth solution above.
EDIT - Nov 28, 2017
As user @Emiel suggests in the comments, the best way to do this would be to use Stream.itearate to drive the list through a sequence of indices:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int skip = 3;
int size = list.size();
// Limit to carefully avoid IndexOutOfBoundsException
int limit = size / skip + Math.min(size % skip, 1);
List<Integer> result = Stream.iterate(0, i -> i + skip)
.limit(limit)
.map(list::get)
.collect(Collectors.toList());
System.out.println(result); // [1, 4, 7, 10]
This approach doesn't have the drawbacks of my previous answer, which comes below (I've decided to keep it for historical reasons).
Another approach would be to use Stream.iterate() the following way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int skip = 3;
int size = list.size();
// Limit to carefully avoid IndexOutOfBoundsException
int limit = size / skip + Math.min(size % skip, 1);
List<Integer> result = Stream.iterate(list, l -> l.subList(skip, l.size()))
.limit(limit)
.map(l -> l.get(0))
.collect(Collectors.toList());
System.out.println(result); // [1, 4, 7, 10]
The idea is to create a stream of sublists, each one skipping the first N elements of the previous one (N=3 in the example).
We have to limit the number of iterations so that we don't try to get a sublist whose bounds are out of range.
Then, we map our sublists to their first element and collect our results. Keeping the first element of every sublist works as expected because every sublist's begin index is shifted N elements to the right, according to the source list.
This is also efficient, because the List.sublist() method returns a view of the original list, meaning that it doesn't create a new List for each iteration.
EDIT: After a while, I've learnt that it's much better to take either one of @sprinter's approachs, since subList() creates a wrapper around the original list. This means that the second list of the stream would be a wrapper of the first list, the third list of the stream would be a wrapper of the second list (which is already a wrapper!), and so on...
While this might work for small to medium-sized lists, it should be noted that for a very large source list, many wrappers would be created. And this might end up being expensive, or even generating a StackOverflowError.
You can build a custom Collector for this task.
Map<String, String> map =
Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
.collect(MappingErrors.collector());
with:
private static final class MappingErrors {
private Map<String, String> map = new HashMap<>();
private String first, second;
public void accept(String str) {
first = second;
second = str;
if (first != null && first.startsWith("err")) {
map.put(first, second);
}
}
public MappingErrors combine(MappingErrors other) {
throw new UnsupportedOperationException("Parallel Stream not supported");
}
public Map<String, String> finish() {
return map;
}
public static Collector<String, ?, Map<String, String>> collector() {
return Collector.of(MappingErrors::new, MappingErrors::accept, MappingErrors::combine, MappingErrors::finish);
}
}
In this collector, two running elements are kept. Each time a String is accepted, they are updated and if the first starts with "err", the two elements are added to a map.
Another solution is to use the StreamEx library which provides a pairMap method that applies a given function to the every adjacent pair of elements of this stream. In the following code, the operation returns a String array consisting of the first and second element of the pair if the first element starts with "err", null otherwise. null elements are then filtered out and the Stream is collected into a map.
Map<String, String> map =
StreamEx.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
.pairMap((s1, s2) -> s1.startsWith("err") ? new String[] { s1, s2 } : null)
.nonNull()
.toMap(a -> a[0], a -> a[1]);
System.out.println(map);
You can write a custom collector, or use the much simpler approach of streaming over the list's indexes:
Map<String, String> result = IntStream.range(0, data.size() - 1)
.filter(i -> data.get(i).startsWith("err"))
.boxed()
.collect(toMap(data::get, i -> data.get(i+1)));
This assumes that your data is in a random access friendly list or that you can temporarily dump it into one.
If you cannot randomly access the data or load it into a list or array for processing, you can always make a custom pairing collector so you can write
Map<String, String> result = data.stream()
.collect(pairing(
(a, b) -> a.startsWith("err"),
AbstractMap.SimpleImmutableEntry::new,
toMap(Map.Entry::getKey, Map.Entry::getValue)
));
Here's the source for the collector. It's parallel-friendly and might come in handy in other situations:
public static <T, V, A, R> Collector<T, ?, R> pairing(BiPredicate<T, T> filter, BiFunction<T, T, V> map, Collector<? super V, A, R> downstream) {
class Pairing {
T left, right;
A middle = downstream.supplier().get();
boolean empty = true;
void add(T t) {
if (empty) {
left = t;
empty = false;
} else if (filter.test(right, t)) {
downstream.accumulator().accept(middle, map.apply(right, t));
}
right = t;
}
Pairing combine(Pairing other) {
if (!other.empty) {
this.add(other.left);
this.middle = downstream.combiner().apply(this.middle, other.middle);
this.right = other.right;
}
return this;
}
R finish() {
return downstream.finisher().apply(middle);
}
}
return Collector.of(Pairing::new, Pairing::add, Pairing::combine, Pairing::finish);
}
No, this is not possible using streams, at least not easily. The stream API abstracts away from the order in which the elements are processed: the stream might be processed in parallel, or in reverse order. So "the next element" and "previous element" do not exist in the stream abstraction.
You should use the API best suited for the job: stream are excellent if you need to apply some operation to all elements of a collection and you are not interested in the order. If you need to process the elements in a certain order, you have to use iterators or maybe access the list elements through indices.
I haven't see such use case with streams, so I can not say if it is possible or not. But when I need to use streams with index, I choose IntStream#range(0, table.length), and then in lambdas I get the value from this table/list.
For example
int[] arr = {1,2,3,4};
int result = IntStream.range(0, arr.length)
.map(idx->idx>0 ? arr[idx] + arr[idx-1]:arr[idx])
.sum();
No, filter does not scan the whole stream. It's an intermediate operation, which returns a lazy stream (actually all intermediate operations return a lazy stream). To convince you, you can simply do the following test:
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.get();
System.out.println(a);
Which outputs:
will filter 1
will filter 10
10
You see that only the two first elements of the stream are actually processed.
So you can go with your approach which is perfectly fine.
However this seems inefficient to me, as the filter will scan the whole list
No it won't - it will "break" as soon as the first element satisfying the predicate is found. You can read more about laziness in the stream package javadoc, in particular (emphasis mine):
Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
Given your restriction you can combine the limit() with custom collector like this:
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));
}
Here Set<Integer> wantedIndices is the set containing the indices of wanted elements (not limited by 2). Usage:
Set<Integer> wantedIndices = new HashSet<>(Arrays.asList(1, 3));
Stream<String> input = Stream.of("s0", "s1", "s2", "s3", "s4");
List<String> result = input.limit(Collections.max(wantedIndices)+1)
.collect(collectByIndex(wantedIndices, Collectors.toList()));
// [s1, s3]
Here's an approach I haven't seen in the other answers. It uses a variation of the ubiquitous Pair class:
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); }
}
Once you have this, you can easily get the first and last elements of a stream. Given an infinite stream and desired indexes, you can use skip() and limit() to trim the stream to contain just the desired elements:
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"));
}
Other variations would include inlining the construction or merge logic into the stream operations instead of having it on the Pair class. Refactor to taste.
You'd use it like this:
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