Java ArrayList的不同排序方法
由于其功能性和灵活性,ArrayList是Java集合框架中使用最为普遍的集合类之一。ArrayList是一种List实现,它的内部用一个动态数组来存储元素,因此ArrayList能够在添加和移除元素的时候进行动态的扩展和缩减。你可能已经使用过ArrayList,因此我将略过基础部分。如果你对ArrayList还不熟悉,你可以参考它的API文档,可以很容易理解在ArrayList上执行基本的操作。
在这篇文章中,我将讨论ArrayList中一种极其重要的操作,你很有可能需要在企业应用开发中实现它。它就是ArrayList元素的排序。
排序字符串对象的ArrayList
考虑一个ArrayList存储着以字符串形式存在的国名(countryname),为了对这个ArrayList进行排序,你需要调用Collections.sort()方法,传递由国名构成的ArrayList对象。这种方法将按照自然顺序(按字母升序)对元素(国名)进行排序。让我们为此来写一段代码。
SortArrayListAscendingDescending.java
packageguru.springframework.blog.sortarraylist.ascendingdescending; importjava.util.ArrayList; importjava.util.Collections; publicclassSortArrayListAscendingDescending{ privateArrayListarrayList; publicSortArrayListAscendingDescending(ArrayListarrayList){ this.arrayList=arrayList; } publicArrayListgetArrayList(){ returnthis.arrayList; } publicArrayListsortAscending(){ Collections.sort(this.arrayList); returnthis.arrayList; } publicArrayListsortDescending(){ Collections.sort(this.arrayList,Collections.reverseOrder()); returnthis.arrayList; } }
在上面的类中,我们在构造器中初始化了一个ArrayList对象。在sortAscending()方法中,我们调用了Collections.sort()方法,并传递这个初始化的ArrayList对象为参数,返回排序后的ArrayList。在sortDescending()方法中,我们调用重载的Collections.sort()方法让其按照降序对元素排序,这个版本的Collections.sort()接收ArrayList对象作为第一个参数,一个由Collections.reverseOrder()方法返回的Comparator对象作为第二个参数。我们将会在稍后讲解Comparator。为了测试排序功能,我们将写一段测试代码。
SortArrayListAscendingDescendingTest.java
packageguru.springframework.blog.sortarraylist.ascendingdescending; importorg.junit.Test; importjava.util.ArrayList; importstaticorg.junit.Assert.*; publicclassSortArrayListAscendingDescendingTest{ <ahref="http://www.jobbole.com/members/madao">@Test</a> publicvoidtestSortAscendingDescending()throwsException{ ArrayListcountryList=newArrayList<>(); countryList.add("France"); countryList.add("USA"); countryList.add("India"); countryList.add("Spain"); countryList.add("England"); SortArrayListAscendingDescendingsortArrayList=newSortArrayListAscendingDescending(countryList); ArrayListunsortedArrayList=sortArrayList.getArrayList(); System.out.println("UnsortedArrayList:"+unsortedArrayList); ArrayListsortedArrayListAscending=sortArrayList.sortAscending(); System.out.println("SortedArrayListinAscendingOrder:"+sortedArrayListAscending); ArrayListsortedArrayListDescending=sortArrayList.sortDescending(); System.out.println("SortedArrayListinDescendingOrder:"+sortedArrayListDescending); } }
在上面的测试代码中,我们创建一个ArrayList对象,并添加了5个字符串对象代表5个国家的名字。然后我们调用getArrayList()、sortAscending()和sortDescending()方法,并打印这些方法返回的ArrayList对象。
到目前为止,所要排序的ArrayList元素都是非常简单的,我们仅仅只是调用Collections.sort()方法并传递了需要排序的ArrayList对象作为参数。但是更多的是你会遇到一些复杂的情景下对ArrayList进行排序。
Collections.sort()方法对ArrayList的元素或者任何其他List的实现提供的可比较的元素进行排序,这意味着这些元素的类需要实现java.lang包中的Comparable接口。正如String类实现了Comparable接口,我们就可以对由国名构成的ArrayList排序。有些其他的标准Java类实现了Comparable接口,包括原始的包装类,例如Integer、Short、Double、Float、Boolean、BigInteger、BigDecimal、File和Date类都实现了Comparable接口。
使用Comparable排序ArrayList
Comparable是带有单一compareTo()方法的接口。一个实现了Comparable接口的类对象可以与其它同类型的对象进行比较,实现Comparable接口的类需要重写compareTo()方法,这个方法接收一个同类型的对象,并实现这个对象和传递给方法的另一个对象比较的逻辑。compareTo()方法返回Int类型的比较结果,分别代表下面的含义:
正值表示当前对象比传递给comPareTO()的对象大
负值表示当前对象比传递给comPareTO()的对象小
零表示两个对象相等
让我们来举一个例子,JobCandidate类的对象保存在ArrayList中并准备对其进行排序。JobCandidate类有三个成员变量:字符串类型的姓名和性别、整型的年龄。我们想要对保存在ArrayList中的JobCandidate对象按照年龄进行排序。因此我们要让JobCandidate类实现Comparable接口并重写compareTo()方法。
JobCandidate类的代码如下:
JobCandidate.java
packageguru.springframework.blog.sortarraylist.comparable; publicclassJobCandidateimplementsComparable{ privateStringname; privateStringgender; privateintage; publicJobCandidate(Stringname,Stringgender,intage){ this.name=name; this.gender=gender; this.age=age; } publicStringgetName(){ returnname; } publicStringgetGender(){ returngender; } publicintgetAge(){ returnage; } @Override publicintcompareTo(JobCandidatecandidate){ return(this.getAge()<candidate.getAge()?-1: (this.getAge()==candidate.getAge()?0:1)); } @Override publicStringtoString(){ return"Name:"+this.name+",Gender:"+this.gender+",age:"+this.age; } }
在上面JobCandidate类被重写的compareTo()方法中,我们实现了基于年龄的比较逻辑。我见过很多程序员将(this.getAge()–candidate.getAge())作为返回的比较结果。尽管使用这种return语句看上去似乎很吸引人,并且也不会对我们的例子造成影响,我的建议是远离这种语句。想象一下,比较整数值,其中有一个或者两个都是负数的结果。这会导致一些错误,让你的程序行为不定,而且更重要的是,这样的错误是很细微的,尤其是在大型的企业应用中很难检测出来。下面我们将写一个辅助类,为委托方对包含了JobCandidate元素的ArrayList对象进行排序。
JobCandidateSorter.java
packageguru.springframework.blog.sortarraylist.comparable; importjava.util.ArrayList; importjava.util.Collections; publicclassJobCandidateSorter{ ArrayListjobCandidate=newArrayList<>(); publicJobCandidateSorter(ArrayListjobCandidate){ this.jobCandidate=jobCandidate; } publicArrayListgetSortedJobCandidateByAge(){ Collections.sort(jobCandidate); returnjobCandidate; } }
在JobCandidateSorter类中,我们初始化了一个ArrayList对象,委托方将通过构造函数实例化JobCandidateSorter。然后我们编写了getSortedJobCandidateByAge()方法,在这个方法中,我们调用Collections.sort()并传递已经初始化了的ArrayList为参数,最后返回排序后的ArrayList。
接下来,我们写一个测试类来测试一下我们的代码。
JobCandidateSorterTest.java
packageguru.springframework.blog.sortarraylist.comparable;
importorg.junit.Test; importjava.lang.reflect.Array; importjava.util.ArrayList; importstaticorg.junit.Assert.*; publicclassJobCandidateSorterTest{ <ahref="http://www.jobbole.com/members/madao">@Test</a> publicvoidtestGetSortedJobCandidateByAge()throwsException{ JobCandidatejobCandidate1=newJobCandidate("MarkSmith","Male",26); JobCandidatejobCandidate2=newJobCandidate("SandyHunt","Female",23); JobCandidatejobCandidate3=newJobCandidate("BettyClark","Female",20); JobCandidatejobCandidate4=newJobCandidate("AndrewStyne","Male",24); ArrayListjobCandidateList=newArrayList<>(); jobCandidateList.add(jobCandidate1); jobCandidateList.add(jobCandidate2); jobCandidateList.add(jobCandidate3); jobCandidateList.add(jobCandidate4); JobCandidateSorterjobCandidateSorter=newJobCandidateSorter(jobCandidateList); ArrayListsortedJobCandidate=jobCandidateSorter.getSortedJobCandidateByAge(); System.out.println("-----SortedJobCandidatebyage:Ascending-----"); for(JobCandidatejobCandidate:sortedJobCandidate){ System.out.println(jobCandidate); } } }
在上面的测试类中,我们创建了四个JobCandidate对象并把它们添加到ArrayList,然后传递这个ArrayList到构造函数来实例化JobCandidateSorter类。最后,我们调用JobCandidateSorter类的getSortedJobCandidateByAge()方法,并打印这个方法返回的排序后的ArrayList。
使用Comparable对ArrayList排序是一种常用的方法。但是你必须知道有某些限制。你想要排序的对象的类必须实现Comparable并覆写compareTo()方法。这基本上意味着你将只能基于一个成员变量来比较对象(我们例子中的年龄字段)。如果要求你按照姓名和年龄来对JobCandidate对象进行排序怎么办?Comparable就不是解决的方法了。另外,比较逻辑是需要进行比较的对象的类的一部分,它消除了比较逻辑可复用性的可能。Java通过使用在java.util包下提供的Comparator接口解决了上述的比较需求。
使用Comparator排序ArrayList
Comparator接口与Comparable接口相似也提供了一个单一的比较方法叫作compare()。然而,与Comparable的compareTo()方法不同的是,这个compare()接受两个同类型的不同对象进行比较。
我们将用Comparator对我们之前使用过的相同JobCandidate类对象进行排序。我们将通过实现Comparatoras匿名内部类,允许对JobCandidate对象按照年龄和姓名进行排序。
下面是使用了Comparator的JobCandidate类代码
JobCandidate.java
packageguru.springframework.blog.sortarraylist.comparator; importjava.util.Comparator; publicclassJobCandidate{ privateStringname; privateStringgender; privateintage; publicJobCandidate(Stringname,Stringgender,intage){ this.name=name; this.gender=gender; this.age=age; } publicStringgetName(){ returnname; } publicStringgetGender(){ returngender; } publicintgetAge(){ returnage; } publicstaticComparatorageComparator=newComparator(){ @Override publicintcompare(JobCandidatejc1,JobCandidatejc2){ return(jc2.getAge()<jc1.getAge()?-1: (jc2.getAge()==jc1.getAge()?0:1)); } }; publicstaticComparatornameComparator=newComparator(){ @Override publicintcompare(JobCandidatejc1,JobCandidatejc2){ return(int)(jc1.getName().compareTo(jc2.getName())); } }; @Override publicStringtoString(){ return"Name:"+this.name+",Gender:"+this.gender+",age:"+this.age; } }
在上面的类中,从29行到35行,我们写了一个匿名类并实现了compare()方法,按照年龄的降序对JobCandidate对象进行排序。从37行到42行,我们又写了一个匿名类并实现了compare()方法,按照姓名的升序对JobCandidate进行排序。现在我们写一个类,为委托方对ArrayList的元素进行排序。
JobCandidateSorter.java
packageguru.springframework.blog.sortarraylist.comparator; importjava.util.ArrayList; importjava.util.Collections; publicclassJobCandidateSorter{ ArrayListjobCandidate=newArrayList<>(); publicJobCandidateSorter(ArrayListjobCandidate){ this.jobCandidate=jobCandidate; } publicArrayListgetSortedJobCandidateByAge(){ Collections.sort(jobCandidate,JobCandidate.ageComparator); returnjobCandidate; } publicArrayListgetSortedJobCandidateByName(){ Collections.sort(jobCandidate,JobCandidate.nameComparator); returnjobCandidate; } }
在上面的类中,我们写了getSortedJobCandidateByAge()方法,在这个方法内部我们调用了Collections.sort()的重载版本,这个版本传递要被排序的ArrayList对象和比较年龄的Comparator对象。在getSortedJobCandidateByName()方法内部,我们又调用了Collections.sort()的另一个重载版本,这个版本传递要被排序的ArrayList对象和比较姓名的Comparator对象。
Let'swriteatestclasstotestourcode.
让我们写一个测试类来测试我们的代码。
JobCandidateSorterTest.java
packageguru.springframework.blog.sortarraylist.comparator; importguru.springframework.blog.sortarraylist.comparator.JobCandidate; importguru.springframework.blog.sortarraylist.comparator.JobCandidateSorter; importorg.junit.Before; importorg.junit.Test; importjava.util.ArrayList; importstaticorg.junit.Assert.*; publicclassJobCandidateSorterTest{ JobCandidateSorterjobCandidateSorter; @Before publicvoidsetUp()throwsException{ JobCandidatejobCandidate1=newJobCandidate("MarkSmith","Male",26); JobCandidatejobCandidate2=newJobCandidate("SandyHunt","Female",23); JobCandidatejobCandidate3=newJobCandidate("BettyClark","Female",20); JobCandidatejobCandidate4=newJobCandidate("AndrewStyne","Male",24); ArrayListjobCandidateList=newArrayList<>(); jobCandidateList.add(jobCandidate1); jobCandidateList.add(jobCandidate2); jobCandidateList.add(jobCandidate3); jobCandidateList.add(jobCandidate4); jobCandidateSorter=newJobCandidateSorter(jobCandidateList); } <ahref="http://www.jobbole.com/members/madao">@Test</a> publicvoidtestGetSortedJobCandidateByAge()throwsException{ System.out.println("-----SortedJobCandidatebyage:Descending-----"); ArrayListsortedJobCandidate=jobCandidateSorter.getSortedJobCandidateByAge(); for(JobCandidatejobCandidate:sortedJobCandidate){ System.out.println(jobCandidate); } } <ahref="http://www.jobbole.com/members/madao">@Test</a> publicvoidtestGetSortedJobCandidateByName()throwsException{ System.out.println("-----SortedJobCandidatebyname:Ascending-----"); ArrayListsortedJobCandidate=jobCandidateSorter.getSortedJobCandidateByName(); for(JobCandidatejobCandidate:sortedJobCandidate){ System.out.println(jobCandidate); } } }
在测试类中我们向ArrayList中添加若干JobCandidate对象,并使用Before注释在测试单元的setup()方法中创建了一个JobCandidateSorter对象。如果你是一个Junit新手,可以参考我以前的文章包括Junit注释(Junit单元测试系列)。在testGetSortedJobCandidateByAge()测试方法中我们调用了getSortedJobCandidateByAge()方法,并打印了该方法返回的排序后的ArrayList。在testGetSortedJobCandidateByName()测试方法中我们调用了getSortedJobCandidateByName()方法并同样打印该方法返回的ArrayList。
在本文中我们看到了ArrayList排序的不同方法。一种是使用Comparable另一种是使用Comparator。方法的选择一直是造成程序员们困惑的原因之一。你最应该记住的就是一个Comparable对象可以说“我可以自己与另外一个对象比较”而一个Comparator对象可以说“我可以比较两个不同的对象”。你不能说一个接口比另一个要好。选择的接口取决于你需要实现的功能。
以上就是本文的全部内容,希望对大家的学习有所帮助。