Сортировать картусначала для Object.property1, а затем для каждого Object.property1, сортировка по Object.property2 - PullRequest
3 голосов
/ 07 апреля 2011

Я использовал приведенный ниже метод для сортировки карты сначала в Object.property1, а затем для каждого Object.property1, сортировки по Object.property2.

например,

property1 = TaxIdNumber и
property2 = ProviderName

Мне просто интересно, что это можно сделать более коротким и точным способом. Буду признателен за любую помощь или предложение.

    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 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;
    }

Ответы [ 2 ]

2 голосов
/ 07 апреля 2011

Ваша нулевая обработка нарушает контракт compare, так как вы считаете null равным любому другому значению, в то время как JavaDoc пишет:

Сравнивает два аргумента для порядка. Возвращает отрицательное целое число, ноль или положительное целое число, поскольку первый аргумент меньше, равен или больше второго.

и, в частности:

Наконец, разработчик должен убедиться, что compare(x, y)==0 подразумевает, что sgn(compare(x, z)) == sgn(compare(y, z)) для всех z.

, который ваш код не может выполнить для x = null, y = "a", z = "b".

Поэтому, если какие-либо объекты или свойства в списке имеют значение null, список может быть отсортирован неправильно.

При этом интересно, может ли список действительно содержать null значений или свойств? Если нет, я бы удалил все проверки null и в итоге получил бы

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());
    }
}

Если список может содержать null объектов или свойств, вы должны определить, являются ли значения null первыми или последними, и соответственно расширить компаратор:

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);
        }
    }
}

Теперь это довольно много повторяющегося и хитрого кода, поэтому сотрудники Apache написали CompareToBuilder . С этим API вы можете просто написать:

@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();
    }
}
2 голосов
/ 07 апреля 2011

Вам нужен только один компаратор. Сначала сравните таксиды. Если они неравны, верните -1 или 1 в зависимости от ситуации. Если они равны, тогда сравните название провайдера.

что-то вроде:

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);
        }
    });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...