1. 程式人生 > >Collections.sort()崩潰分析

Collections.sort()崩潰分析

對List排序一般採用兩種方法:
(1)實體類實現Comparable<T>介面,完成compareTo(T o)方法。
(2)建立一個Comparator<T>的實現類,完成compare(T o1, T o2)方法,然後利用這個比較器對List進行排序。
詳情可以看一下我的這篇文章
當我們書寫compareTo(T o)compare(T o1, T o2)方法時,我們知道:當第一個引數小於第二個引數時返回負整數;當第一個引數等於第二個引數時返回0;當第一個引數大於第二個引數時返回正整數。
但一定要記住3個原則:
1. 確保:sgn(compare(x, y)) == -sgn(compare(y, x)).


2. 確保:如果((compare(x, y)>0) && (compare(y, z)>0)),那麼compare(x, z)>0.
3. 確保:如果compare(x, y)==0,那麼對於任意的z都有sgn(compare(x, z))==sgn(compare(y, z))成立.

sgn(expression)是一個數學正負號函式,根據expression是負數、0、正數分別返回-1、0、1。

可以不實現equals()方法,也不一定要求(compare(x, y)==0) == (x.equals(y))。但建議正確實現各個方法。

由於JDK7之前Collections.sort

Arrays.sort所使用的排序演算法並沒有對此做嚴格的限制,所以可以在x、y相等的時候不返回0,但JDK7之後採用更高效的TimSort演算法進行排序,就需要嚴格遵循上面的3個原則,否則就會出現下面這樣的異常:

Exception in thread “main” java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(Unknown Source)
at java.util.TimSort.mergeAt(Unknown Source)
at java.util.TimSort.mergeForceCollapse(Unknown Source)
at java.util.TimSort.sort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at java.util.ArrayList.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at cn.test.JDK7.main(JDK7.java:32)

該異常並不是總會出現,只有當集合中元素順序影響到演算法排序時才會出現(如TimSort在Merge過程中出觸發了“勝利N次”優化,並且A[base1]==A[base1+x])&&(A[base1+x]==B[base2]時,具體可以看一下原始碼中mergeLo()和mergeHi()方法的實現),所以在開發環境下很難復現這個異常,通常只會在產品環境下發現。所以為了遵循原則,也是為了養成良好的程式設計習慣,一定要正確實現比較器。
下面是一個會出現異常的Case:

package cn.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class JDK7 {

    private static final String SORT_TIME_VALUE = "sort_time_value";
    private static final String PRICE_DOLLAR = "price_dollar";
    private static ArrayList<GameBean> gameBeans = new ArrayList<>();

    public static void main(String[] args) {
        add("0", 7);
        add("3", 1);
        add("0", 25);
        add("1", 2);
        add("0", 2);
        add("1", 1);
        add("0", 1);
        add("1", 1);
        add("0", 4);
        add("1", 1);
        add("0", 2);
        add("1", 1);
        add("0", 3);
        add("2", 1);
        add("1", 1);
        Collections.sort(gameBeans, new GameComparator(SORT_TIME_VALUE, 1));
        for (GameBean gameBean : gameBeans) {
            System.out.println(gameBean.getTime());
        }
    }

    private static void add(String time, int count) {
        for (int i = 0; i < count; i++) {
            GameBean gameBean0 = new GameBean();
            gameBean0.setTime(time);
            gameBean0.setPrice(time);
            gameBeans.add(gameBean0);
        }
    }

    static class GameComparator implements Comparator<GameBean> {

        String mSortField;
        int mSortType;// 1:升序 -1:降序

        public GameComparator(String sortField) {
            this(sortField, 1);
        }

        public GameComparator(String sortField, int sortType) {
            this.mSortField = sortField;
            this.mSortType = sortType;
        }

        @Override
        public int compare(GameBean lhs, GameBean rhs) {
            int ret = 0;
            if (SORT_TIME_VALUE.equals(mSortField)) {
                Long x = Long.valueOf(lhs.getTime());
                Long y = Long.valueOf(rhs.getTime());
                ret = mSortType * x.compareTo(y);
            } else if (PRICE_DOLLAR.equals(mSortField)) {
                Float x = Float.valueOf(lhs.getPrice());
                Float y = Float.valueOf(rhs.getPrice());
                ret = mSortType * x.compareTo(y);
            }
            return ret;
            // return (Integer.valueOf(lhs.getTime()) > Integer.valueOf(rhs.getTime())) ? 1 : -1;
        }
    }

    static class GameBean {

        private String time;
        private String price;

        public String getTime() {
            return time;
        }

        public void setTime(String time) {
            this.time = time;
        }

        public String getPrice() {
            return price;
        }

        public void setPrice(String price) {
            this.price = price;
        }

    }

}

注意:

  • 不要用三目運算子返回. o1 > o2 ? 1 : -1沒有考慮相等的情況,如果o1等於o2就違背了3條原則。
  • 不要將long強轉成int. 如果long超過了int最大值會導致資料錯誤,進而影響排序。
  • 不要忽略比較的傳遞性. 尤其是比較邏輯很複雜時。

想了解更多,可以看一下下面幾篇文章:

相關推薦

Collections.sort()崩潰分析

對List排序一般採用兩種方法: (1)實體類實現Comparable<T>介面,完成compareTo(T o)方法。 (2)建立一個Comparator<T>的實現類,完

jdk7 Collections.sort()方法報錯分析

問題背景 起因 前些天測試給提了一個專案裡的bug,在檢視專案的一個線上資料的列表的時候發生了崩潰.然後我根據bugly定位發現是在使用Collection.sort()對list排序的時候產生Comparison method violates its general cont

windowsclient崩潰分析和調試

而在 ros lag this指向 eas 位置 call 即使 多個實例 本文介紹windows上崩潰分析的一些手段,順便提多進程調試、死鎖等。 1.崩潰分析過程 1.1 確認錯誤碼 不管是用windbg還是用vs。首先應該註意的是錯誤碼,而90%以上的崩潰都是非法

Collections.sort詳解

排序。 根據 強行 strong null equals() cti ron 順序 Collections.sort(list, new PriceComparator());的第二個參數返回一個int型的值,就相當於一個標誌,告訴sort方法按什麽順序來對list進行排序

java中Collections.sort排序詳解

比較器 元素 .net 字符 atp style pri com 實現接口 Comparator是個接口,可重寫compare()及equals()這兩個方法,用於比價功能;如果是null的話,就是使用元素的默認順序,如a,b,c,d,e,f,g,就是a,b,c,d,e,f

java基礎——Collections.sort的兩種用法

基本類 == 輸出結果 code sed java 意思 size htm Collections是一個工具類,sort是其中的靜態方法,是用來對List類型進行排序的,它有兩種參數形式: public static <T extends Comparabl

java Collections.sort()實現List排序的默認方法和自定義方法

public get object 順序 text main 輸出 any 比較 1.java提供的默認list排序方法 主要代碼: List<String> list = new ArrayList();list.add("劉媛媛"); list.add("王

Hadoop自帶Sort例子分析

lan exit more double expr ogr oms lru sort /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor lic

collections.sort對javabean的list進行排序

collection.sort javabean 1、collection.sort排序算法底層實際是 將集合轉換成array,再執行arrays.sort,arrays.sort利用歸並排序,優化的快排,timSort等方式。2、對string類型數據排序public static void col

04-java.util.Collections+Collections.sort()方法的練習

1、java.util.Collections:集合框架的工具類,裡面的方法都是靜態方法。此類完全由在collection上進行操作或返回collection的靜態方法組成。如果為此類的方法所提供的collection或類物件為null,則這些方法都將丟擲NullPointerException

Java的陣列和list升序,降序,逆序函式Collections.sort和Arrays.sort的使用

list升序,降序,逆序List<Integer>list =new ArrayList<Integer>();//如果list是 5 7 2 6 8 1 41、升序:Collections.sort(list) //list: 1 2 4 5 6 7 82、降序:Collection

android Collections.sort排序的一種使用

//按照javabean的時間排序 Collections.sort(infosList, new Comparator<Dzbp_Info>() { @Override public int compare(Dzbp_Info lhs, Dzbp_Info rhs)

關於集合迭代器以及Collections.sort(元素)的一點事

/*一般Collections.sort()用來排序泛型為Integer的集合,若要排序其他型別,必須要 讓該類實現Comparable<T>介面 */ public class day implements Comparable<day> { private in

collections.sort()預設排序規則

預設按ASCII碼排序,一位一位的比較,應該排了3次 第一次比較第一位全部是a,所以順序沒變 第二次第二位排序[a0, a1, a12, a11, a10, a2, a3, a4, a5, a6, a7, a8, a9] 第三次第三位排序[a0, a1, a10, a11, a12, a2, a

List排序Collections.sort 重寫compare

1 static List<Integer> intList = Arrays.asList(2,5,7, 3, 1); 2 3 public static void main(String[] args) { 4 5

實現List集合排序的兩種方法(使用Collections.sort方法)

1:實現comparable package core.java.collection.collections;      public class User implements Comparable<User>{              private i

Java中Collections.sort()的使用

在日常開發中,很多時候都需要對一些資料進行排序的操作。然而那些資料一般都是放在一個集合中如:Map ,Set ,List 等集合中。他們都提共了一個排序方法 sort(),要對資料排序直接使用這個方法就行,但是要保證集合中的物件是 可比較的。 怎麼讓一個物件是

Collections.sort()方法和lambda表示式結合實現集合的排序

1.使用Collections.sort()實現集合的排序,這裡的方法具體指的是:   Collections.sort(List list, Compatator c)       第一個引數:為要進行排序的集合。     第二個引數:Compatator的實現,指定排序的方式。   2

JAVA Collections.sort方法在SSH三大框架中使用中的問題

     最近,一同學在開發中遇到了SSH三大框架中使用到了Collections.sort方法。然而,他開發環境中的JDK 是1.7.0_64,網站部署的JDK版本是1.7.0_80,他通過開發環境中產生的.class直接去更新網站部署環境中的.class 檔案後,程式

Collections.sort的兩種用法

Collections是一個工具類,sort是其中的靜態方法,是用來對List型別進行排序的,它有兩種引數形式: public static <T extends Comparable<? super T>> void sort(List