Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return the N last elements in Java Streams [duplicate]

This question is just for fun.

I have this method:

private static String getBaseDomain(String fullDomain) {
    // we take the base domain from the usual xxx.yyy.basedomain.tld: we 
    // want only the last 2 elements: basedomain.tld
    List<String> elements = Arrays.asList(fullDomain.split("\\."));
    if( elements.size() > 2){
        elements = elements.subList(elements.size()-2, elements.size());
    }
    return String.join(".", elements);
}

And I'm wondering how to get the same result using the java stream API (actually, I'm wondering which method would be the most resource-efficient).

I can't figure how to get only the last 2 elements from a stream: limit(2) will give me the first two, and for skip(XXX) I don't know how to extract the size of the stream "inline".

Can you tell me how you would do?

like image 209
Gui13 Avatar asked Sep 05 '25 21:09

Gui13


2 Answers

You could use skip:

elements.stream().skip(elements.size() - 2)

From the API:

Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned.

Probably useless example:

// a list made of a, b, c and d
List<String> l = Arrays.asList("a", "b", "c", "d");

// prints c and d
l.stream().skip(l.size() - 2).forEach(System.out::println);

Probably useless note:

As mentioned by a few, this only works if you have a size to work with, i.e. if you're streaming from a collection.

Quoting Nicolas, a stream doesn't have a size.

like image 166
Mena Avatar answered Sep 08 '25 10:09

Mena


You could do a bit of inlining of your original approach, which I think shortens it up nicely, and you don't even have to use a stream:

    String[] a = fullDomain.split("\\.");
    return String.join(".", Arrays.asList(a)
                                  .subList(Math.max(0, a.length-2), a.length));

If you really want to use a stream, you can use the array-subrange stream source:

    String[] a = fullDomain.split("\\.");
    return Arrays.stream(a, Math.max(0, a.length-2), a.length)
                 .collect(Collectors.joining("."));

If all you have is a stream, whose size you don't have in advance, I'd just dump the elements into an ArrayDeque:

    final int N = 2;
    Stream<String> str = ... ;

    Deque<String> deque = new ArrayDeque<>(N);
    str.forEachOrdered(s -> {
        if (deque.size() == N) deque.removeFirst();
        deque.addLast(s);
    });
    return String.join(".", deque);

Of course, this isn't as general as writing a collector, but for simple cases it's probably just fine.

like image 39
Stuart Marks Avatar answered Sep 08 '25 10:09

Stuart Marks