-1

I am trying to understand how to work with TreeSet with Comparator implementation. Code is below.

The objects from the numbers Set should be sorted according to the following rules:

  • If one range is longer than others, then it is the larger in the sorting order.
  • If several ranges have the same length, the smaller is the one with the smaller left border.
  • The sorting goes in the ascending order.

*The left border of ranges is always less than the right one, but their values can be negative as well.

The problem is that TreeSet can contain only unique values, so current code cut some objects with the same range in a result output. So it seems that I need to add an additional condition with this comparing(), but I am stuck with what exactly condition should be there. I believe that there is a way to change only getComparator() method here.

import java.util.*;

class LongRange {

    public static void main(String[] args) {
        Set<LongRange> numbers = new TreeSet<>(LongRange.getComparator());

        numbers.add(new LongRange(0, 5));
        numbers.add(new LongRange(2, 4));
        numbers.add(new LongRange(1, 4));
        numbers.add(new LongRange(1, 7));
        numbers.add(new LongRange(3, 5));
        numbers.add(new LongRange(-10, 1));
        numbers.add(new LongRange(-20, -9));
        numbers.add(new LongRange(-21, -10));

        numbers.forEach(System.out::println);
    }

    private final long left;
    private final long right;

    public static Comparator<LongRange> getComparator() {
        return Comparator.comparing(a -> Math.subtractExact(a.right, a.left));
    }

    public LongRange(long left, long right) {
        this.left = left;
        this.right = right;
    }

    public long getLeft() {
        return left;
    }

    public long getRight() {
        return right;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (other == null || getClass() != other.getClass()) {
            return false;
        }
        LongRange longRange = (LongRange) other;
        return left == longRange.left &&
                right == longRange.right;
    }

    @Override
    public int hashCode() {
        return Objects.hash(left, right);
    }

    @Override
    public String toString() {
        return String.format("%d %d", left, right);
    }
}
rsnlpn
  • 95
  • 1
  • 10
  • 2
    Why is 1, 7 after -10, 1 in the correct result? Shouldn't ranges with a larger length, like -10, 1 come later? – Sweeper Jan 15 '23 at 13:01
  • @Sweeper you are right, question updated and I think that there should be some fixes also with abs() for options with negative values. I'll take a look. – rsnlpn Jan 15 '23 at 13:07
  • 2
    Create method which calculates distance, something like `private long getDistance(){ return right - left; }` (although it could cause integer overflow for large numbers so you may want to handle that). With this you could write Comparator like `Comparator.comparingLong(LongRange::getDistance).thenComparingLong(LongRange::getLeft);`. Anyway since you are supposed to first sort based on *length* in *ascending* order (from smallest to largest) then `2 4` which has length `2` should be *before* `-10 1` which has length `11`. In case of *equal* length like `2 4` vs `3 5` the `2 4` should be first. – Pshemo Jan 15 '23 at 13:07
  • Umm you didn't update anything about the correct result or the rules... – Sweeper Jan 15 '23 at 13:08
  • I think something like the following would reflect your description of the requirements pretty well? `Comparator. comparingInt(a -> a.right - a.left).thenComparingInt(a -> a.left)`. I could be missing something, but maybe you can adjust for that? – Ole V.V. Jan 15 '23 at 13:08
  • 1
    @OleV.V. The type arguments for `thenComparing` probably needs to be explicitly written ([as usual](https://stackoverflow.com/q/24436871/5133585)) lol – Sweeper Jan 15 '23 at 13:16
  • @Pshemo yes, thank you, it works. But is there a way how to use the logic part of getInstance() method inside Function in Comparator? For example in another comment here there is some proposed solution, but in that case I see 'Cannot resolve symbol 'right'' error and it appears when I add another thenComparing condition. – rsnlpn Jan 15 '23 at 13:17
  • 1
    @Sweeper [Yes, obviously](https://stackoverflow.com/a/40502858/5772882). Thanks for reminding me (and possibly others). – Ole V.V. Jan 15 '23 at 13:21
  • @OleV.V. got it, thank you. Comparator.comparingLong((LongRange a) -> a.right- a.left).thenComparingLong((LongRange a)->a.left); You mean this, right? – rsnlpn Jan 15 '23 at 13:27

1 Answers1

2

Summarizing comments from Ole V.V. and applying them for my case, solution is presented below. The tricky thing for me in this case was that I need to declare a explicitly - (LongRange a).

public static Comparator<LongRange> getComparator() {
       return Comparator.comparingLong((LongRange a) -> a.right- a.left)
               .thenComparingLong((LongRange a) -> a.left);
    }
rsnlpn
  • 95
  • 1
  • 10
  • 2
    You only need the explicit type on the first call, `Comparator.comparingLong((LongRange a) -> a.right - a.left) .thenComparingLong(a -> a.left)`. Altenatively, you can specify the (generic) type argument instead of the parameter type: `Comparator.comparingLong(a -> a.right - a.left) .thenComparingLong(a -> a.left)` – Holger Jan 16 '23 at 11:59