Reputation: 1211
Is there a one liner to get the last 5 elements of a LinkedHashSet in a new LinkedHashSet?
This is what I currently have, but it's not very efficient:
new LinkedHashSet<String>(new LinkedList<String>(set)
.subList(Math.max(0, set.size() - 5), set.size());
Or should I use for this case a TreeSet, SortedSet, HashSet ?
Upvotes: 1
Views: 1500
Reputation: 21315
This can be efficiently achieved using the reversed()
method added to LinkedHashSet
in Java 21 as part of the SequencedSet
interface.
LinkedHashSet<String> lastFive = new LinkedHashSet<>(set.reversed().stream()
.limit(5).toCollection(LinkedHashSet::new).reversed());
This can be simplified to use the reversed
Set
directly if the returned set doesn't need to be a LinkedHashSet
, but just a mutable Set
with a well-defined iteration order and near-identical characteristics of a LinkedHashSet
:
SequencedSet<String> lastFive = set.reversed().stream()
.limit(5).toCollection(LinkedHashSet::new).reversed();
Upvotes: 0
Reputation: 1211
I ended up using this:
com.google.common.collect.EvictingQueue<E>
with this you get to keep only the last x elements.
EvictingQueue<String> queue = EvictingQueue.create(5);
Upvotes: 0
Reputation: 719376
Here's the problem. The iterators for a LinkedHashSet
are unidirectional; i.e. you can't iterate backwards, even though the underlying data structure has a doubly-linked list. That means to get the last N you need to iterate to the end of the list. That is O(N)
.
In your algorithm, the LinkedList
constructor is copying the set into a new data structure using (probably) the iterator.
By contrast, the TreeSet
API has an descendingIterator()
method that returns an Iterator
that iterates backwards through the list. If you use that correctly, you can get the last 5 elements of the set in O(1)
. The downside is that adding elements to the set will be O(logN)
instead of O(1)
for a hash-based set.
Upvotes: 0
Reputation: 253
Using ArrayList
you could get a better performance:
long s1 = System.nanoTime();
LinkedHashSet<String> last5 = new LinkedHashSet<String>(new LinkedList<String>(set)
.subList(Math.max(0, set.size() - 5), set.size()));
System.out.println(System.nanoTime() - s1);
s1 = System.nanoTime();
LinkedHashSet<String> usingArrayList = new LinkedHashSet<String>(new ArrayList<String>(set)
.subList(Math.max(0, set.size() - 5), set.size()));
System.out.println(System.nanoTime() - s1);
Upvotes: 0
Reputation: 511
If you use Java 8 and you are ok to get back a HashSet
(instead of a LinkedHashSet
), you can use the Stream API:
Set<String> newSet = set.stream()
.skip(set.size() - 5)
.collect(Collectors.<String>toSet());
Upvotes: 0