2
myData{
itemId,
location,
ExpDtTm,
userId
}

I trying to sort a TreeSet like so:

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

But there is a problem if any of the dates are equivalent they wont be added to the tree set which proves to be a problem. If anyone knows what to do help is much appreciated.

I also tried

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

But it stopped excepting myData in both of the lambdas.

TOTOROCATBUS
  • 172
  • 1
  • 2
  • 17
  • 3
    Then you need to consider it (add few more fields that determine the equality) in your comparator – Thiyagu Jul 16 '18 at 16:14
  • 1
    If you just need you data (with duplicates) sorted, consider using a `List` and sort that. – Andreas Jul 16 '18 at 16:16
  • 1
    Why do you use a TreeSet rather than a List, if you want duplicates? – JB Nizet Jul 16 '18 at 16:19
  • @JBNizet The project that populates the set I get these values from returns a set and I don't have the access to change this. I just trying to work with what I'm given. – TOTOROCATBUS Jul 16 '18 at 16:29
  • 1
    Not sure I understand: you get a Set from some project. What prevents you from putting all the values of the set inside a list and to sort the list? – JB Nizet Jul 16 '18 at 16:31

4 Answers4

2

The reason this happens is because the comparator is used to not only to determine the order but also to determine whether the two objects are equal (instead of hashCode, which a HashSet would use):

a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal.

If you have two objects that aren't, in fact, identical but just happen to have the same date, you could use the object's identity hash code (effectively, it's memory address) as a secondary sorting condition:

TreeSet<myData> sorted = 
    new TreeSet<>(Comparator.comparing(myData -> myData.ExpDtTm)
                            .thenComparingInt(System::identityHashCode));
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • Wait... why not `Object::hashCode`, which might be the identity hashcode, but might not be? – Michael Jul 16 '18 at 16:45
  • Feel free to edit what I've added. I didn't think it was worth adding a 3rd answer, and yours was the best so far. – Michael Jul 16 '18 at 16:52
  • @Michael two non-equal objects can have the same `hashCode()`, so I felt that `System.identityHashCode()` was safer. – Mureinik Jul 16 '18 at 17:11
  • If you compare on hashcode/identity hashcode, how does this provide any sorting on the object's `ExpDtTm` field? Why not use a `HashMap`? Same unordered result. – M. le Rutte Jul 16 '18 at 17:11
  • @M.leRutte the sorting is done first by `ExpDtTm`, and then, only for equal `ExpDtTm`s, ties are broken according to the identity hash code. – Mureinik Jul 16 '18 at 17:13
  • 1
    A HashSet doesn't use hashCode() to determine if two objects are equal. It uses equals(). hashCode is only used to reduce the number of potentially equal objects (i.e. choose the bucket). – JB Nizet Jul 16 '18 at 17:30
  • Why can't I just create a new comparator?? – TOTOROCATBUS Jul 16 '18 at 17:56
  • @Mureinik two non-equal objects can have the same identity hash code as well. Any hash code is by definition vulnerable to collisions – Michael Jul 16 '18 at 20:03
2

Employee.Java

private String name;

public Employee(String name) {
    this.name = name;
}
public void setName(String name) {
    this.name = name;
}

public String getName() {
    return this.name;
}

public String toString() {
    return "Employee[ id=" + name + "]";
}

Driver Class

TreeSetTest.Java

    TreeSet<Employee> treeSet = new TreeSet<>(new Comparator<Employee>() {
        @Override
        public int compare(Employee o1, Employee o2) {
            int index = o1.getName().compareTo(o2.getName());
            return index==0?1:index;
        }
    });
    treeSet.add(new Employee("Harvansh"));
    treeSet.add(new Employee("Harvansh1"));
    treeSet.add(new Employee("Harvansh"));
    treeSet.add(new Employee("Harvansh2"));
    treeSet.add(new Employee("Harvansh"));
    treeSet.add(new Employee("Harvansh3"));
    treeSet.add(new Employee("Harvansh"));
    treeSet.forEach(System.out::println);

Output

Employee[ id=Harvansh]

Employee[ id=Harvansh]

Employee[ id=Harvansh]

Employee[ id=Harvansh]

Employee[ id=Harvansh1]

Employee[ id=Harvansh2]

Employee[ id=Harvansh3]

Here Is The Complete Solution With Output

1

A TreeSet is an implementation of Set, which explicitly forbids duplicate entries by design. If you want a sorted collection that includes duplicates, use an implementation of List (e.g. ArrayList), and sort it at the end by calling Collections.sort(myList, Comparator.comparing(myData -> myData.ExpDtTm));.

user31601
  • 2,482
  • 1
  • 12
  • 22
  • Sorry I changed the question a little but It doesn't have duplicates of the key I just want to sort by the Expiration. – TOTOROCATBUS Jul 16 '18 at 16:24
  • 2
    @TOTOROCATBUS your comparator defines that two keys are equal if they have the same expiry date. The comparator doesn't just define the order. It also defines equality. – JB Nizet Jul 16 '18 at 16:29
  • 2
    @TOTOROCATBUS I suggest you read the [documentation of `TreeSet`](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/TreeSet.html) carefully, and ensure you understand what you're working with. – user31601 Jul 16 '18 at 16:32
  • Believe me I have read it along with LinkHashSet, HashSet, and SortedSet I'm just trying to sort without removing duplicates and I have to use some kinda set so if you have any suggestions for a better set feel free. – TOTOROCATBUS Jul 16 '18 at 16:45
  • @TOTOROCATBUS It's not at all clear why you "have to use some kinda set" -- do you need to pass it into something that accepts a `Set` after you've sorted it? – David Conrad Jul 16 '18 at 17:56
  • @DavidConrad Ok I'm sorry for my unclarity. yes I have to pass the set to a ruby workflow which is only setup to handle sets. – TOTOROCATBUS Jul 16 '18 at 18:05
1

This is what I ended up doing:

final SortedSet<myData> sorted = new TreeSet<>(new Comparator<myData>() {
            @Override
            public int compare(myData o1, myData o2) {
                 int comparedValue = o1.expDtTm.compareTo(o2.expDtTm);
                 if(comparedValue == 0){
                       comparedValue = 1;
                 }
            }
});

Inside of that comparedValue if statement you can add any other variables you might want to sort with as well.

TOTOROCATBUS
  • 172
  • 1
  • 2
  • 17