2010-06-15 3 views
42

Мне нужен метод, который принимает List<T>, где T реализует Comparable и возвращает true или false в зависимости от того, отсортирован ли список.Как определить, отсортирован ли список на Java?

Каков наилучший способ реализовать это в Java? Очевидно, что дженерики и подстановочные знаки предназначены для работы с такими вещами легко, но я все запутался.

Было бы неплохо иметь аналогичный метод, чтобы проверить, находится ли список в обратном порядке.

+0

Есть ли вероятность, что вы можете использовать отсортированную коллекцию? Или просто позвоните .Sort() по мере необходимости? – Juliet

+0

Целью этого является тестирование, поэтому нет. –

+0

- список, который должен находиться в порядке возрастания или убывания? –

ответ

86

Guava обеспечивает эту функциональность, благодаря своей удивительной Ordering класса. Ordering - это Comparator ++. В этом случае, если у вас есть список некоторого типа, который реализует Comparable, вы могли бы написать:

boolean sorted = Ordering.natural().isOrdered(list); 

Это работает для любого Iterable, а не только List, и вы можете справиться с null s легко, указав, должны ли они прийти до или после любых других не null элементов:

Ordering.natural().nullsLast().isOrdered(list); 

Кроме того, так как вы упомянули, что вы хотели бы быть в состоянии проверить обратный порядок, а также нормальны, что это будет сделано как:

Ordering.natural().reverse().isOrdered(list); 

Java 8 пользователей: Используйте эквивалент Comparators#isInOrder(Iterable) вместо этого, так как все остальные упорядоченности в основном устарели (как объяснено в классе documentation).

+8

И есть также 'isStrictlyOrdered()', если вы хотите убедиться, что дубликатов нет. –

+9

+1 для указания существующего кода, чтобы люди перестали изобретать вещи и начали создавать новые вещи. –

+0

Можете ли вы показать пример этого, если нет естественного порядка, и вы хотите перейти в «Компаратор»? Я думаю, что это то, что «Ordering.from» для: «Возвращает порядок на основе существующего экземпляра компаратора. Обратите внимание, что нет необходимости создавать новый анонимный внутренний класс, реализующий Comparator, чтобы передать его здесь. Вместо этого просто подкласс Ordering and реализуйте его метод сравнения напрямую ». –

5

Чтобы проверить, является ли список или любая структура данных в этом отношении задачей, которая занимает только время O (n). Просто перебирайте список с помощью интерфейса Iterator и пропустите данные (в вашем случае вы уже имеете его готовый как тип Comparable) от начала до конца, и вы можете найти, было ли оно отсортировано или нет.

+1

Интересно, что ваш алгоритм на самом деле является O (1) ожидаемым временем для разумно случайных списков. – mikera

+1

Это было бы O (n) в худшем случае (когда список действительно отсортирован). – Jesper

+4

-1, так как заявитель указал, что он «запутывается» в вопросах реализации, таких как дженерики и подстановочные знаки, я думаю, что этот ответ говорит только то, что он уже знает. –

1

Это который будет принимать O (n) время (наихудший случай). Вам нужно будет обрабатывать два случая: список сортируется в порядке убывания и список сортируется в порядке возрастания.

Вам нужно будет сравнить каждый элемент со следующим элементом, убедившись, что заказ сохранен.

16

Easy:

List tmp = new ArrayList(myList); 
Collections.sort(tmp); 
boolean sorted = tmp.equals(myList); 

Или (если элементы сравнимы):

Object prev; 
for(Object elem : myList) { 
    if(prev != null && prev.compareTo(elem) > 0) { 
     return false; 
    } 
    prev = elem; 
} 
return true; 

Или (если элементы не сравнимы):

Object prev; 
for(Object elem : myList) { 
    if(prev != null && myComparator.compare(prev,elem) > 0) { 
     return false; 
    } 
    prev = elem; 
} 
return true; 

Реализации неудачу для списков, содержащих нулевые значения. В этом случае вам необходимо добавить соответствующие проверки.

+0

Collections.sort потребует, чтобы элементы списка были уже сопоставимыми, и это было оговоркой в ​​вопросе в любом случае. – Yishai

+3

Я думаю, что в вашем первом примере отсутствует строка, так как 'tmp' не заполняется. –

+1

Первый пример также довольно расточительный для того, что он делает, а Объекты во втором должны быть сопоставимыми. – ColinD

3

Просто используйте итератор, чтобы просмотреть содержимое List<T>.

public static <T extends Comparable> boolean isSorted(List<T> listOfT) { 
    T previous = null; 
    for (T t: listOfT) { 
     if (previous != null && t.compareTo(previous) < 0) return false; 
     previous = t; 
    } 
    return true; 
} 
+3

Это продолжается, потому что 'предыдущий' никогда не будет установлен. См. Ответ Даниила для правильного потока. – BalusC

+0

К сожалению, спасибо, что указали это. Я починил это. (@BalusC) – jjnguy

+0

Необходимо было бы, конечно, ограничить ''. – ColinD

2

Это то, что я хотел бы сделать:

public static <T extends Comparable<? super T>> boolean isSorted(List<T> list) { 
    if (list.size() != 0) { 
     ListIterator<T> it = list.listIterator(); 
     for (T item = it.next(); it.hasNext(); item = it.next()) { 
      if (it.hasPrevious() && it.previous().compareTo(it.next()) > 0) { 
       return false; 
      } 
     } 

    } 
    return true; 
} 
22

Вот общий метод, который будет делать трюк:

public static <T extends Comparable<? super T>> 
     boolean isSorted(Iterable<T> iterable) { 
    Iterator<T> iter = iterable.iterator(); 
    if (!iter.hasNext()) { 
     return true; 
    } 
    T t = iter.next(); 
    while (iter.hasNext()) { 
     T t2 = iter.next(); 
     if (t.compareTo(t2) > 0) { 
      return false; 
     } 
     t = t2; 
    } 
    return true; 
} 
0

Одна простая реализация на массивы:

public static <T extends Comparable<? super T>> boolean isSorted(T[] a, int start, int end) { 
    while (start<end) { 
     if (a[start].compareTo(a[start+1])>0) return false; 
     start++; 
    } 
    return true; 
} 

Преобразование для списки:

public static <T extends Comparable<? super T>> boolean isSorted(List<T> a) { 
    int length=a.size(); 
    if (length<=1) return true; 

    int i=1; 
    T previous=a.get(0); 
    while (i<length) { 
     T current=a.get(i++); 
     if (previous.compareTo(current)>0) return false; 
     previous=current; 
    } 
    return true; 
} 
+1

стр. обратите внимание, что реализация намеренно разработана, чтобы избежать выделения итератора или создания нескольких вызовов. Это предназначено для обычного случая, когда вы используете ArrayList, для другой реализации списка вам, вероятно, лучше использовать итератор. – mikera

+0

Одна вещь, которая может быть сделана в этом случае, - проверить, реализует ли «List» «RandomAccess» и использует эту реализацию, если это так, и реализацию «Итератор», если нет. Вы бы не хотели использовать это с помощью LinkedList. – ColinD

+0

@ColinD отличная идея! Я предполагаю, что вы можете сделать это так, чтобы он обнаружил это при компиляции, а также – mikera

2
private static <T extends Comparable<? super T>> boolean isSorted(List<T> array){ 
    for (int i = 0; i < array.size()-1; i++) { 
     if(array.get(i).compareTo(array.get(i+1))> 0){ 
      return false; 
     } 
    } 
    return true; 
} 

zzzzz не уверен, ребята, что вы делаете, но это можно сделать в простой для цикла.

+0

fwiw, реализация библиотеки в принятом ответе (Guava) по существу такова: https://github.com/google/guava/blob/ e7700e6f78d9594d1ee37d43bf4e0aa1e58b4e7a/guava/src/com/google/common/collect/ordering.java # L895 Но ИМО всегда лучше держать свою кодовую базу маленькой, а не переопределять вещи, которые может дать вам библиотека. –

+0

Это намного лучше, чем сортировка массива и проверка, равны ли они. – ShellFish

0

Если вам это нужно для модульного тестирования, вы можете использовать AssertJ. Он содержит утверждение, чтобы проверить, если список отсортирован:

List<String> someStrings = ... 
assertThat(someStrings).isSorted(); 

Существует также альтернативный метод, который принимает isSortedAccordingTo компаратор в случае, если вы хотите использовать пользовательский компаратор для сортировки.

7

Если вы используете Java 8, потоки могут помочь.

list.stream().sorted().collect(Collectors.toList()).equals(list); 

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

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

+1

Nice ... очевидно, не помог 2010 году использовать Java 6, но это хорошее дополнение. –

+0

Гораздо менее эффективно, чем идеальное решение без распределения и без сортировки. – Vadzim

+0

Согласно Java 8 docs, Collectors.toList(); Нет гарантий относительно типа, изменчивости, сериализуемости или безопасности потоков {@code List}; Вы должны использовать поставщика ArrayList – danidacar