开发者

Sort a Map<String, Object> first on Object.property1 and then for each Object.property1, sort by Object.property2

I have used the below method to Sort a Map first on Object.property1 and then for each Object.property1, sort by Object.property2.

for example,

property1 = TaxIdNumber and

property2 = ProviderName

I was just wondering this can be done in a more shorter and precise manner. Any help or suggestion would be appreciated.

    private List<TestObject> sortByValue(final Map m) {
        List<TestObject> values = new ArrayList<TestObject>();
        values.addAll(m.values());

        // First sort the list by Tax ID.
        Collections.sort(values, new Comparator<TestObject>() {
            public int compare(TestObject r1, TestObject r2) {
                Long taxId1 = (r1 == null ? null : r1.getTaxIdNumber());
                Long taxId2 = (r2 == null ? null : r2.getTaxIdNumber());

                if (taxId1 == null || taxId2 == null) {
                    return 0;
                }

                return taxId1.compareTo(taxId2);
            }
        });

        // Then 开发者_StackOverflow社区sort the list by Provider name.
        Collections.sort(values, new Comparator<TestObject>() {
            public int compare(TestObject r1, TestObject r2) {
                String name1 = (r1 == null ? null : r1.getProviderName());
                String name2 = (r2 == null ? null : r2.getProviderName());

                if (name1 == null || name2 == null) {
                    return 0;
                }

                if (r1.getTaxIdNumber() == r2.getTaxIdNumber()) {
                    return name1.compareTo(name2);
                } else {
                    return 0;
                }
            }
        });

        return values;
    }


You only need one comparator. first compare the taxids. If they are unequal return -1 or 1 as appropriate. If they are equals, then compare the provider name.

something like:

Collections.sort(values, new Comparator<TestObject>() {
        public int compare(TestObject r1, TestObject r2) {
            Long taxId1 = (r1 == null ? null : r1.getTaxIdNumber());
            Long taxId2 = (r2 == null ? null : r2.getTaxIdNumber());

            if (taxId1 == null || taxId2 == null) {
                return 0;
            }

            int cmp = taxId1.compareTo(taxId2);

            if (cmp != 0)
                return cmp;

            String name1 = (r1 == null ? null : r1.getProviderName());
            String name2 = (r2 == null ? null : r2.getProviderName());

            if (name1 == null || name2 == null) {
                return 0;
            }

            return name1.compareTo(name2);
        }
    });


Your null-handling violates the contract of compare, as you deem null equal to any other value, while the JavaDoc writes:

Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

and in particular:

Finally, the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.

which your code fails to accomplish for x = null, y = "a", z = "b".

Therefore, if any objects or properties in the list are null, the list may not be sorted correctly.

That being said, I wonder if the list may really contain null values or properties? If not, I'd remove all null checks and end up with

Collections.sort(list, new Comparator<TestObject>() {
    @Override public int compare(TestObject o1, TestObject o2) {
        int c = o1.getTaxIdNumber().compareTo(o2.getTaxIdNumber);
        if (c != 0) {
            return c;
        }
        return o1.getProviderName().compareTo(o2.getProviderName());
    }
}

If the list may contain null objects or properties, you must define whether the null values come first or last, and extend the comparator accordingly:

Collections.sort(list, new Comparator<TestObject>() {
    @Override public int compare(TestObject o1, TestObject o2) {
        // insert null-checks for o1, o2 here

        int c = cmp(getTaxIdNumber(), o2.getTaxIdNumber());
        if (c != 0) {
            return c;
        }
        return cmp(o1.getProviderName(), o2.getProviderName());
    }

    private <T extends Comparable<? super T>> cmp(T o1, T o2) {
        if (o1 == o2) {
            return 0;
        else if (o1 == null) {
            return -1;
        } else if (o2 == null) {
            return 1;
        } else {
            return o1.compareTo(o2);
        }
    }
}

Now this is quite a bit of repetitive and tricky code, which is why the folks over at Apache wrote the CompareToBuilder. With that API, you can simply write:

@Override int compare(TestObject r1, TestObject r2) {
    // insert null checks for r1 and r2 here - if you really need them

    return new CompareToBuilder()
        .append(r1.getTaxIdNumber(), r2.getTaxIdNumber())
        .append(r1.getProviderName(), r2.getProviderName())
        .toComparison();
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜