JDK1.7 Collections.sort Arrays.sort发现的问题

使用jdk1.7后发现的collections.sort(List list)排序问题和浅析

 

       自己的电脑系统重装以后,装了一个最新的jdk(1.7),当然了,本地eclipse的编译等级仍然是1.5。

之后有一个功能是需要调用服务端的一个方法来达到对ArrayList排序的功能,大部分朋友应该都知道java.util.Collections下有一个sort(List list)方法,当然了,List中的对象是需要实现Comparable接口并自行编写compareTo方法来实现比较的,一般会用Collections来对List进行排序的朋友都知道应该这么做。

       平时做算法的情况比较少,虽然知道可以用Collections来排序List,但自己用的也不多,在编写完客户端代码以后,本地测试完了,发现了问题,好像排序的顺序和自己的预期相反,当时也以为自己可能记错了jdk的api,所以随后自己在compareTo方法的返回中将1和-1进行了调换,好,现在本地的结果是对的,本地是jdk1.7的环境。

       本地测试通过以后,在把代码放到服务器上进行测试,这时候发现的情况让我非常惊讶,排序结果竟又和本地不同,花了不少时间,一直以为代码的替换有些问题,在确认不是因为代码替换带来的问题后,检查了一下服务器端的jdk版本,是1.5的,感觉问题可能出在Collections.sort(List list)上,而Collections.sort是调用Arrays.sort(Object[] a)来实现的。

       为避免其他可能的问题带来的干扰,我写了一个单独的简单类分别在本地(1.7)和服务器(1.5)环境上进行测试:

 

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TempObj implements Comparable {
	private int value;
	public TempObj() {
	}
	public TempObj(int value) {
		this.value = value;
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	public int compareTo(TempObj o) {
		System.out.println(this.getValue());
		System.out.println(o.getValue());
		return 0;
	}
        public static void main(String[] args){
		List list = new ArrayList();
		list.add(new TempObj(1));
		list.add(new TempObj(2));
		Collections.sort(list);
	}
}

       执行后,发现本地(1.7)和服务器(1.5)在compareTo中打印的结果也是完全相反的,换句话说,toCompare的实现中,对于待比较的两个参数完全相反(由打印出的this.getValue()和o.getValue()看出),如此看来自然会影响我对toCompare的实现返回结果了(1和-1)。

 

 

       再查看jdk1.7与jdk1.5在该方法上的实现,发现确实有些不同。

       关于1.7下Arrays.sort(Object[] a)的实现:

 

public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a);
    }
    /** To be removed in a future release. */
    private static void legacyMergeSort(Object[] a) {
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }

 而1.5下Arrays.sort(Object[] a)的实现:

 

 

public static void sort(Object[] a) {
        Object[] aux = (Object[])a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }

 我注意到1.7下的sort有一个分支判断,当LegacyMergeSort.userRequested为true的情况下,采用legacyMergeSort,否则采用ComparableTimSort。LegacyMergeSort.userRequested的字面意思大概就是“用户请求传统归并排序”的意思,这个分支调用的是与jdk1.5相同的方法来实现功能。

       我没有花时间去研究jdk1.7为什么要这么做,以及在另外一个分支判断中 ComparableTimSort的具体实现是如何的,我只需要尽量解决当前发现的问题,网上搜索过一下后,发现在执行Collections.sort进行排序前可以调用:

 

System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");

 

来实现设置LegacyMergeSort.userRequested的赋值,测试过以后确实有效,这样能够在jdk1.7下强制使用老的排序方式达到预期功能。

       其他的问题:

       1、如果客户生产环境中的某些软件需要升级需要变更(升级)到高版本的jdk,还是可能会遇到不少难以预期的问题,例如像我遇到的排序结果本末倒置,所以升级真是挺危险的一件事。

       2、在jdk1.7的legacyMergeSort方法注释中,注意到了:“To be removed in a future release.”,这么说在1.7以后的版本中,老的mergeSort方法可能会被ComparableTimSort替代,这样也就不能通过改变userLegacyMergeSort的值来影响排序的实现了,这可能确实会带来一些问题,如果需要进行自定义逻辑的排序,那么以后也需要留意。

       3、我不确定这算不算一个Bug,因为目前也没有仔细了解 ComparableTimSort 的实现,发现问题后目前在网上也没有搜索到对这个问题的任何描述,但是这确实对我的程序有很大的影响,因为在toCompare中比对和被比对参数的调换必然影响到用户的自定义判断;另外我也检查了在jdk1.6下的实现与1.5相同,也就是说,是1.7才开始采用这个方式的,本地的jdk1.7具体版本是jdk-7u3-windows-x64。

 

你可能感兴趣的:(JAVA,JDK,1.7)