Ordering Set by Instant
Asked Answered
B

5

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.

Boreas answered 13/7, 2018 at 17:26 Comment(6)
Please use an ordered data structure, such as a List. So use Collectors.toList() after sorting.Dallas
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?Feldt
TreeSet has a constructor that takes a Comparator.Thoer
@Thoer But Collectors.toSet() doesn't take a set factory. So you won't know which exact Set implementation you getDallas
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.Polonaise
@Dallas Correct. But you can use Collectors.toCollection(() -> new TreeSet<>(comparator))Thoer
W
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; }
     );
Wisconsin answered 13/7, 2018 at 17:37 Comment(1)
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.Thoer
T
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.

Thoer answered 13/7, 2018 at 19:4 Comment(0)
P
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]
Popular answered 13/7, 2018 at 17:41 Comment(0)
B
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
Boreas answered 16/7, 2018 at 14:1 Comment(0)
H
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.

Headed answered 17/7, 2018 at 10:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.