【问题标题】:How to determine if a List is sorted in Java?如何确定列表是否在 Java 中排序?
【发布时间】:2011-03-04 02:06:08
【问题描述】:

我想要一个采用List<T> 的方法,其中T 实现Comparable 并返回truefalse,具体取决于列表是否已排序。

在 Java 中实现此功能的最佳方法是什么?很明显,泛型和通配符旨在能够轻松处理这些事情,但我很纠结。

如果有一个类似的方法来检查列表是否是倒序的,那就太好了。

【问题讨论】:

  • 您是否有机会使用排序集合代替?或者只是根据需要调用 .Sort()?
  • 这样做的目的是为了测试,所以没有。
  • 列表应该是升序还是降序?
  • 我希望能够同时检查这两种类型,因为两种类型都会出现。
  • @fuzzy lollipop -- 我希望以类型安全的方式实现此方法。

标签: java generics sorting wildcard


【解决方案1】:

Guava 通过其Comparators 类提供此功能。

boolean sorted = Comparators.isInOrder(list, comparator);

还有Ordering 类,虽然这已经基本过时了。 OrderingComparator++。在这种情况下,如果你有一个实现Comparable 的类型的列表,你可以这样写:

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

这适用于任何Iterable,而不仅仅是List,您可以通过指定它们是否应该出现在任何其他非null元素之前或之后来轻松处理nulls:

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

此外,由于您提到您希望能够检查逆序以及正常,因此可以这样完成:

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

【讨论】:

  • 如果你想确保没有重复,还有isStrictlyOrdered()
  • +1 用于指向现有代码,以便人们停止重新发明事物并开始创造新事物。
  • 如果没有自然排序并且你想传入Comparator,你能举个例子吗?我认为这就是Ordering.from 的用途:“根据现有的比较器实例返回一个排序。请注意,没有必要创建一个新的匿名内部类来实现 Comparator 只是为了将它传递到这里。相反,只需子类化 Ordering 并实现它直接比较方法。”
  • @tieTYT:你可以使用Ordering.from(comparator).isOrdered(list);但是,如果您自己实现比较器,则最好扩展 Ordering 而不是首先实现 Comparator,在这种情况下它只是 myOrdering.isOrdered(list)
  • 在这里留个便条。大多数订购已过时,但他们在此处删除了计划删除的 cmets。 github.com/google/guava/commit/…
【解决方案2】:

如果您使用的是Java 8 或更高版本,streams 可能会有所帮助。

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

更简单地说,在Java 16+ 中,使用Stream#toList

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

此代码会将列表排序,并将其元素收集到另一个列表中,然后将其与初始列表进行比较。如果两个列表包含相同位置的相同元素,则比较将成功。

请注意,这种方法的空间和时间复杂度会比其他方法更差,因为它必须对列表进行不适当的排序,因此不应该将它用于非常大的列表。但它是最容易使用的,因为它是单个表达式,不涉及第三方库。

【讨论】:

  • 很好......显然没有帮助 2010 年我使用 Java 6,但这是一个很好的补充。
  • 远不如理想的无分配和无排序解决方案有效。
  • 根据 Java 8 文档,Collectors.toList();不保证返回的 {@code List} 的类型、可变性、可序列化性或线程安全性;您应该使用 ArrayList 的供应商
  • 看起来这与这种情况无关,因为生成的列表永远不会超过 is 语句,因此它永远不会被序列化、变异或从多个线程访问。
【解决方案3】:

这里有一个通用的方法可以解决问题:

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

【讨论】:

    【解决方案4】:

    简单:

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

    或者(如果元素具有可比性):

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

    或者(如果元素不可比较):

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

    对于包含空值的列表,实现会失败。在这种情况下,您必须添加适当的检查。

    【讨论】:

    • Collections.sort 要求列表元素已经具有可比性,无论如何这是问题中的规定。
    • 我认为您的第一个示例缺少一行,因为未填充 tmp
    • 第一个例子也很浪费,第二个中的对象需要是 Comparable 的。
    • @Daniel prev 需要初始化吗?
    • @MosesKirathe:是的
    【解决方案5】:

    如果你需要它进行单元测试,你可以使用AssertJ。它包含一个断言来检查列表是否已排序:

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

    还有一种替代方法isSortedAccordingTo 采用比较器,以防您想使用自定义比较器进行排序。

    【讨论】:

      【解决方案6】:

      检查一个列表或任何数据结构是否是一个只需要 O(n) 时间的任务。只需使用 Iterator 接口遍历列表并从头到尾遍历数据(在您的情况下,您已经准备好作为 Comparable 的类型),您可以轻松找到它是否已排序

      【讨论】:

      • 有趣的是,您的算法实际上是合理随机列表的 O(1) 预期时间。
      • 在最坏的情况下(当列表确实排序时)将是 O(n)。
      • -1 因为提问者表示他在泛型和通配符等实现问题上“纠结”了,我认为这个答案只是告诉他他已经知道的事情。
      【解决方案7】:
      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 不确定你们在做什么,但这可以通过简单的 for 循环来完成。

      【讨论】:

      • fwiw,在接受的答案 (Guava) 中库的实现基本上是这样的:github.com/google/guava/blob/… 但 IMO 总是更好地保持你的代码库小而不是重新实现库可以给你的东西。
      • 这比对数组进行排序并检查它们是否相等要好得多。
      【解决方案8】:

      只需使用迭代器查看List&lt;T&gt; 的内容。

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

      【讨论】:

      • 这会继续,因为previous 永远不会被设置。请参阅丹尼尔的答案以了解正确的流程。
      • 当然也需要限制为&lt;T extends Comparable&gt;
      • @Colin(和其他所有人)我很抱歉这不能编译。我写得很快。 Colin,你有编辑权限,请随时修复我犯的这个错误。
      • 我已经接受了避免使用原始类型的解决方案。不过,这是我的第二选择,所以我投了赞成票。
      • @FarmBoy,感谢您的反馈!很高兴我们能提供帮助。
      【解决方案9】:

      使用 Java 8 流:

      boolean isSorted = IntStream.range(1, list.size())
              .map(index -> list.get(index - 1).compareTo(list.get(index)))
              .allMatch(order -> order <= 0);
      

      这也适用于空列表。然而,它只对同时实现RandomAccess 标记接口的列表有效(例如,ArrayList)。

      如果您无权访问流的底层集合,可以使用以下丑陋的 hack:

      Stream<T> stream = ...
      Comparator<? super T> comparator = ...
      boolean isSorted = new AtomicInteger(0) {{
          stream.sequential()
                .reduce((left, right) -> {
                     getAndUpdate(order -> (order <= 0) ? comparator.compare(left, right) : order);
                     return right;
                });
      }}.get() <= 0;
      

      【讨论】:

        【解决方案10】:

        这是一个需要 O(n) 时间(最坏情况)的操作。您将需要处理两种情况:列表按降序排序,以及列表按升序排序。

        您需要将每个元素与下一个元素进行比较,同时确保保留顺序。

        【讨论】:

          【解决方案11】:

          这就是我会做的:

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

          【讨论】:

          • 它不起作用。 ``` public static void main(String[] args) { List list = Arrays.asList(1,2,9,7,4); System.out.println(isSorted(list)); } ``` 期望打印为假,但打印为真。
          【解决方案12】:

          一个简单的数组实现:

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

          【讨论】:

          • 附注请注意,该实现是故意设计的,以避免分配迭代器或多次调用获取。这是为您使用 ArrayList 的常见情况而设计的,对于其他列表实现,您可能最好使用迭代器。
          • 在这种情况下可以做的一件事是检查List 是否实现RandomAccess,如果是则使用此实现,否则使用Iterator 实现。您不会真的想将其与 LinkedList 原样使用。
          • @ColinD 好主意!我猜你可以这样做,以便它在编译时也检测到这一点
          【解决方案13】:

          这里有一个使用IterableComparator 的方法。

          <T> boolean isSorted(Iterable<? extends T> iterable,
                               Comparator<? super T> comparator) {
              boolean beforeFirst = true;
              T previous = null;
              for (final T current : iterable) {
                  if (beforeFirst) {
                      beforeFirst = false;
                      previous = current;
                      continue;
                  }
                  if (comparator.compare(previous, current) > 0) {
                      return false;
                  }
                  previous = current;
              }
              return true;
          }
          

          还有一个用于ComparableIterable 的方法和一个用于排序的标志。

          <T extends Comparable<? super T>> boolean isSorted(
                  Iterable<? extends T> iterable, boolean natural) {
              return isSorted(iterable, natural ? naturalOrder() : reverseOrder());
          }
          

          【讨论】:

            【解决方案14】:

            IMO,据我所见,检查这一点的复杂性与第二次执行排序一样糟糕,而且我知道如果没有排序,人们就会短路返回,但如果列表实际上是排序,然后将遍历整个长度...,我主张让代码继续,就好像 List 已排序一样,并让代码本身确定是否排序。

            我建议放置一个错误,详细说明为什么会失败的特定行会这样做,因为它的集合没有反转/排序等...

            例如。

            detachTailIndex.run() 行将给出 NullPointerException(),

            if (fromReversedMethod && detachTailIndex == null) {
            throw new NullPointerException("detachTailIndex was null. \n 
            Reason: The List<> is required to be reversed before submission")
            }
            

            或者,如果您的计划是在排序时使用条件,您可以使用 null 来验证它是否已排序并选择其他选项。

            如果没有其他选项,则使用其他响应即可。

            这是另一个例子:

                int key = entry.getKey();
                
                List<Integer> shouldBeReversed = entry.getValue();
            
                try {
                    //If all entries need to be iterated better place them inside the Try-Catch
                        updateBatch(key,
                                xes -> {
                                    for (int rowIndex : shouldBeReversed
                                    ) {
                                        xes.remove((int) rowIndex); // will return an IndexOutOfBoundsException if not sorted in reverse
                                    }
                                    return xes;
                                }
                        );
                } catch (IndexOutOfBoundsException e) {
                    throw new IndexOutOfBoundsException(e.getMessage() + "\n" +
                            "Reason: Batch indexes (Entry's value \"List<Integer>\") should be sorted in reverse order before submission");
                }
            

            如果需要条件(已排序或未排序),则 catch() body 可以用作“未排序”选项。

            当然,这是非常具体的情况,但似乎检查排序顺序只是为了得到一个布尔值似乎太多了(?),特别是在迭代时。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-12-25
              • 2020-02-23
              • 2013-12-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多