Home > Net >  Create comparator from another of different type
Create comparator from another of different type

Time:10-29

I have a very simple helper class that looks something like:

private static class IntIdx {
    int val;
    int idx;
    IntIdx(int val, int idx) {
        this.val = val;
        this.idx = idx;
    }
    public int getValue() {
        return val;
    }
    public int getIdx() {
        return idx; 
    }
}

And I use a comparator that sorts it by value then by index:

static public List<Integer> decode(List<Integer> al) {

    final Comparator<IntIdx> idxComp = Comparator
        .comparingInt(IntIdx::getValue)
        .thenComparingInt(IntIdx::getIdx);

 ...[other code]...
}

I now have a situation where the values must be sorted in a different order, so I want to allow the caller to pass in a Comparator<Integer> for the value part. The problem is I don't know how to create such a comparator using similar syntax to above. Because the caller doesn't need to be aware of the implementation details, they are not passing a Comparator<IntIdx>, they are passing a Comparator<Integer> and I don't know how to chain together comparators for IntIdx using a comparator for Integer.

I can write something like:

static public List<Integer> decode(List<Integer> al, Comparator<Integer> valComp) {

    final Comparator<IntIdx> testComp = (i1, i2) -> {
            int r = valComp.compare(i1.getValue(), i2.getValue());
            if (r != 0) {
                return r;
            }
            return Integer.compare(i1.getIdx(), i2.getIdx());
        };

 ...[other code]...
}

which should work, but it feels much messier than what I currently have. Is this the best way of writing this? Is there any way to write the above is the nice Comparator.comparing... syntax in the current implementation?

It feels like there should be something like the following (which I understand doesn't work)

final Comparator<IntIdx> testComp = Comparator.compare(valComp.compare(IntIdx::getValue)).thenComparingInt(IntIdx::getIdx)

or

final Comparator<IntIdx> testComp = valComp.comparing(IntIdx::getValue).thenComparingInt(IntIdx::getIdx);

although I can't find anything like that in the Comparator documentation or my Google/SO searches.

I assume this is a relatively common thing and I just don't know the correct terminology to search for. I look forward to face-palming when the answer is revealed.

If the caller passed in a Comparator<IntIdx>, it would be easy to chain it together like

final Comparator<IntIdx> iiComp = valComp.thenComparingInt(IntIdx::getIdx);

but that's not my scenario.

CodePudding user response:

Implementing Comparator by writing a convoluted multiline lambda is error-prone. Leveraging Java 8 factory methods is always a preferred way to define a Comparator, you simply need to use another static method.

Replace comparingInt() with comparing(Function, Comparator), which allows to provide a Comparator that should be used to compare the result produced by the specified Function:

static public List<Integer> decode(List<Integer> al,
                                   Comparator<Integer> valComp) {
    
    final Comparator<IntIdx> idxComp = Comparator
        .comparing(IntIdx::getValue, valComp)
        .thenComparingInt(IntIdx::getIdx);
    
}
  • Related