5

I have a Java Set that I give information to:

Set<myData> dataLocations = getData(location);

I would like to sort this Set I have tried a sortedSet and couldn't get it to work, so I tried this

dataLocations = dataLocations.stream().sorted(Comparator.comparing(myData -> myData.expDtTm)).collect(Collectors.toSet());

The only problem is that in the Java documentation it doesn't guarantee retaining any orders. So I tried this:

TreeSet<myData> sortedDataLocations = dataLocations.stream().sorted(Comparator.comparing(myData -> myData.expDtTm)).collect(Collectors.toCollection(TreeSet<myData>));

needless to say it didn't work so anyone that has any other ideas they would be much appreciated.

TOTOROCATBUS
  • 172
  • 1
  • 2
  • 17
  • 1
    Please use an ordered data structure, such as a `List`. So use `Collectors.toList()` after sorting. – ernest_k Jul 13 '18 at 17:32
  • Basic definition of `myData`, I guess an instance of a custom class? Define *couldn't get it to work* any exceptions, error messages or a specific order of elements? – 0x1C1B Jul 13 '18 at 17:33
  • 4
    `TreeSet` has [a constructor](https://docs.oracle.com/javase/10/docs/api/java/util/TreeSet.html#%3Cinit%3E(java.util.Comparator)) that takes a `Comparator`. – Slaw Jul 13 '18 at 17:33
  • @Slaw But `Collectors.toSet()` doesn't take a set factory. So you won't know which exact `Set` implementation you get – ernest_k Jul 13 '18 at 17:37
  • A `TreeSet` with a `Comparator` as shown in the answer below is a good way to handle this. Something else to add to your toolbox: `LinkedHashSet` is a `Set` implementation whose iteration order is the same as the element insertion order. – dnault Jul 13 '18 at 17:41
  • @ernest_k Correct. But you can use `Collectors.toCollection(() -> new TreeSet<>(comparator))` – Slaw Jul 13 '18 at 18:36

5 Answers5

8

You can use TreeSet and provide a comparator

TreeSet<myData> sorted = new TreeSet<>(Comparator.comparing(MyData::expDtTm));
sorted.addAll(dataLocations);

Or as described in Collector class Javadocs create your own collector for TreeSet:

Collector<Widget, ?, TreeSet<Widget>> intoSet =
     Collector.of(
         TreeSet::new, 
         TreeSet::add,
         (left, right) -> { left.addAll(right); return left; }
     );
Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • 5
    Instead of creating a custom `Collector` directly, you can simply use `Collectors.toCollection(() -> new TreeSet<>(yourComparator));`. It takes care of the accumulating and combining for you. – Slaw Jul 13 '18 at 18:50
3

Your third attempt was close though as written doesn't compile. The method Collectors.toCollection takes a Supplier that returns the desired Collection, not the Collection itself.

If MyData was defined as:

public class MyData {
  private Instant instant;
  public Instant getInstant() { return instant; }
  public void setInstant(Instant instant) { this.instant = instant; }
}

Then in order to collect them into a SortedSet via Streams you could do:

Comparator<MyData> comparator = Comparator.comparing(MyData::getInstant);
TreeSet<MyData> set = getMyData().stream()
            .collect(Collectors.toCollection(() -> new TreeSet<>(comparator));

Note that I don't use Stream.sorted here. It is actually detrimental if you were to use Stream.sorted because it adds work that doesn't help the end result. The Stream would sort its elements and then later start adding them to the TreeSet which will also sort the elements.

That said, there are cases where using Stream.sorted would be beneficial: when Stream.collect returns a Collection that guarantees insertion order. A LinkedHashSet, as well as any implementation of List, guarantee insertion order. So you could do:

LinkedHashSet<MyData> set = getMyData().stream()
            .sorted(comparator)
            .collect(Collectors.toCollection(LinkedHashSet::new));
// Or use a List
List<MyData> list = getMyData().stream()
            .distinct() // If you want only unique elements in the end List
            .sorted(comparator)
            .collect(Collectors.toList());

Note: It is not enough that the end Collection guarantees insertion order. The Collector being used must not have unordered as a characteristic. This is the case with the Collectors returned by Collectors.toCollection and Collectors.toList. It is not the case when using Collectors.toSet.

Slaw
  • 37,820
  • 8
  • 53
  • 80
1

You try this:

public class Example {
  public static void main(String[] args) {
    Comparator<String> stringComparator =
      Comparator.comparing((String x) -> x);

    Supplier<TreeSet<String>> supplier =
      () -> new TreeSet<>(stringComparator);

    Set<String> set = new HashSet<>(Arrays.asList("1", "3", "7", "2", "9", "4"));
    TreeSet<String> treeSet = set.stream()
      .collect(Collectors.toCollection(supplier));
    System.out.println(treeSet);
  }
}

Replace String class to yours.

Output

[1, 2, 3, 4, 7, 9]
Maxim Kasyanov
  • 938
  • 5
  • 14
0

What I ended up doing was:

TreeSet<myData> sorted = new TreeSet<>(Comparator.comparing(myData -> myData.ExpDtTm);

Now this is not the best answer and I'm still looking for another on because if you have 2 ExpDtTm that are the same the comparator will delete the second Instant meaning:

| Lot Number | ExpDtTm             |
| LOT-4      | 2018-07-16 12:41:56 |
| LOT-7      | 2018-07-16 12:41:56 |

This will cause LOT-7 to be deleted and not returned to user.

NOTE: This is also the time that happens when formatting an Instant to just have a date which I did 
for testing purposes
TOTOROCATBUS
  • 172
  • 1
  • 2
  • 17
0
    Comparator<MyData> instantComparator = Comparator
            .comparing(MyData::getExpDtTm)
            .thenComparing(MyData::getLotNo);
    SortedSet<MyData> sorted = new TreeSet<>(instantComparator);
    sorted.addAll(dataLocations);

I tried this with the following set:

LOT-9 2018-07-15T10:39:53Z
LOT-1 2018-07-17T14:46:57Z
LOT-4 2018-07-16T12:41:56Z
LOT-7 2018-07-16T12:41:56Z

After sorting it became:

LOT-9 2018-07-15T10:39:53Z
LOT-4 2018-07-16T12:41:56Z
LOT-7 2018-07-16T12:41:56Z
LOT-1 2018-07-17T14:46:57Z

A set is a mathematical set, it may contain each element only once. A SortedSet like a TreeSet uses its comparator (or the natural ordering of elements if no comparator is supplied) to decide whether two elements are equal and hence cannot both be in the set. So in order to sort the elements by the Instant and still keep elements with the same Instant we need to distinguish them some other way. So in my comparator I have added sorting by lot no. after Instant. If lot no. isn’t unique either, you will want to add a further list of attributes on which to sort.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161