8

I have a Set of some objects. I need to get the two min objects from the Set.

My example is as follows:

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Example {
     public static void main( String[] args ) {
         SomeObject obj1 = new SomeObject(1);
         SomeObject obj2 = new SomeObject(2);
         SomeObject obj3 = new SomeObject(3);
         Set<SomeObject> set = Stream.of(obj1,obj2,obj3)
                                     .collect(Collectors.toSet());

         SomeObject minObject= set.stream()
                                  .min(Comparator.comparingInt(object->object.getValue()))
                                  .get();
         System.out.println(minObject);
     }
}


class SomeObject{
    private int value;
    SomeObject(int value){
            this.value=value;
    }

    public int getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.value+"";
    }
}

In my example I can get the min object from this Set based on the value attribute.

So, I need to get the two min values using Java stream operations.

The answers should be fast because in my real program I call this method many times and my sets are very large.

SDJ
  • 4,083
  • 1
  • 17
  • 35
xmen-5
  • 1,806
  • 1
  • 23
  • 44
  • A custom implementation of a [priority queue with fix size(here 2)](https://stackoverflow.com/questions/7878026/is-there-a-priorityqueue-implementation-with-fixed-capacity-and-custom-comparato) should help. Wrote a similar implementation for one of our use-cases, let me know if you're interested in using it. – Naman Dec 24 '18 at 22:15

2 Answers2

4

You can get the second min value as follows:

 set.stream()
    .filter(s -> s.getValue() != minObject.getValue())     
    .min(Comparator.comparingInt(object -> object.getValue()))
    .get();
  • This streams over the set of elements again ensuring that we're ignoring the previous min value via filter.
  • Then we get the min value via min

or you can get both at the same time:

Stream<SomeObject> twoMinValues = set.stream()
                   .sorted(Comparator.comparingInt(object -> object.getValue()))
                   .limit(2);
  • This streams over the set of elements, sorting the entire sequence from smallest to largest and then taking the first two which is definitely less efficient than the above approach due to the behaviour of the "sorted" intermediate operation.

The awnser should be fast because In my real program i call this method many times and my Sets are very Large.

As for needing a "fast" program, I'd recommend that you first try to solve the problem at hand just using the typical imperative approach as in most cases they're faster than using streams.

if it's not much better then "consider" using parallel streams if and only if you know that you can leverage parallelism.

see Should I always use a parallel stream when possible?

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • do you have any good books/suggestions/ideas to master java stream operations? – xmen-5 Dec 24 '18 at 19:30
  • 2
    @zakzak Well people learn differently, I've certainly got a collection of Java streams books e.g. "Java-8 in action", "core java for the impatient" etc.. you can also find a lot of tutorials online as well as a lot of youtube videos already out there, you should give it a try. but the most important part of all is **practice** ,**practice** and **practice** and this site has certainly helped me a lot in that regard (by answering questions). – Ousmane D. Dec 24 '18 at 19:35
2

You can pass your Set<E> to an instance of NavigableSet<E>, then you can poll the first two (lowest) elements from it :

final NavigableSet<SomeObject> navigableSet = new TreeSet<>(Comparator.comparingInt(SomeObject::getValue));

navigableSet.addAll(set);

final SomeObject firstMin = navigableSet.pollFirst();

final SomeObject secondMin = navigableSet.pollFirst();
HPH
  • 388
  • 2
  • 11