java教程

Java List排序方法具体讲

位置:首页 > java教程 > java技巧,2018-03-10 17:47
在这篇文章中,我将讨论 ArrayList 中一种极其重要的操作,你很有可能需要在企业应用开发中实现它。它就是 ArrayList 元素的排序。排序字符串对象的

在这篇文章中,我将讨论 ArrayList 中一种极其重要的操作,你很有可能需要在企业应用开发中实现它。它就是 ArrayList 元素的排序。

排序字符串对象的 ArrayList

考虑一个 ArrayList 存储着以字符串形式存在的国名(country name),为了对这个 ArrayList 进行排序,你需要调用 Collections.sort()方法,传递由国名构成的 ArrayList 对象。这种方法将按照自然顺序(按字母升序)对元素(国名)进行排序。让我们为此来写一段代码。
SortArrayListAscendingDescending.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
packageguru.springframework.blog.sortarraylist.ascendingdescending;
 importjava.util.ArrayList;
 importjava.util.Collections;
 publicclassSortArrayListAscendingDescending {
 privateArrayList arrayList;
 publicSortArrayListAscendingDescending(ArrayList arrayList) {
 this.arrayList = arrayList;
 }
 publicArrayList getArrayList() {
 returnthis.arrayList;
 }
 publicArrayList sortAscending() {
 Collections.sort(this.arrayList);
 returnthis.arrayList;
 }
 publicArrayList sortDescending() {
 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
packageguru.springframework.blog.sortarraylist.ascendingdescending;
 importorg.junit.Test;
 importjava.util.ArrayList;
 importstaticorg.junit.Assert.*;
 publicclassSortArrayListAscendingDescendingTest {
 publicvoidtestSortAscendingDescending()throwsException {
 ArrayList countryList =newArrayList<>();
 countryList.add("France");
 countryList.add("USA");
 countryList.add("India");
 countryList.add("Spain");
 countryList.add("England");
 SortArrayListAscendingDescending sortArrayList =newSortArrayListAscendingDescending(countryList);
 ArrayList unsortedArrayList = sortArrayList.getArrayList();
 System.out.println("Unsorted ArrayList: "+ unsortedArrayList);
 ArrayList sortedArrayListAscending = sortArrayList.sortAscending();
 System.out.println("Sorted ArrayList in Ascending Order : "+ sortedArrayListAscending);
 ArrayList sortedArrayListDescending = sortArrayList.sortDescending();
 System.out.println("Sorted ArrayList in Descending Order: "+ sortedArrayListDescending);
 }
 }

在上面的测试代码中,我们创建一个 ArrayList 对象,并添加了 5 个字符串对象代表 5 个国家的名字。然后我们调用 getArrayList()、sortAscending()和 sortDescending()方法,并打印这些方法返回的 ArrayList 对象。
输出如下:

-------------------------------------------------------
T E S T S
-------------------------------------------------------  
Running guru.springframework.blog.sortarraylist.ascendingdescending.SortArrayListAscendingDescendingTest  
Unsorted ArrayList: [France, USA, India, Spain, England]
Sorted ArrayList in Ascending Order : [England, France, India, Spain, USA]
Sorted ArrayList in Descending Order: [USA, Spain, India, France, England]  
Tests run:1, Failures:0, Errors:0, Skipped:0, Time elapsed:0.001sec - in guru.springframework.blog.sortarraylis

到目前为止,所要排序的 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()方法。
The code of the JobCandidate class is this.
JobCandidate类的代码如下:
JobCandidate.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
packageguru.springframework.blog.sortarraylist.comparable;
 publicclassJobCandidateimplementsComparable {
 privateString name;
 privateString gender;
 privateintage;
 publicJobCandidate(String name, String gender,intage) {
 this.name = name;
 this.gender = gender;
 this.age = age;
 }
 publicString getName() {
 returnname;
 }
 publicString getGender() {
 returngender;
 }
 publicintgetAge() {
 returnage;
 }
 @Override
 publicintcompareTo(JobCandidate candidate) {
 return(this.getAge() < candidate.getAge() ? -1:
 (this.getAge() == candidate.getAge() ?0:1));
 }
 @Override
 publicString toString() {
 return" Name: "+this.name +", Gender: "+this.gender +", age:"+this.age;
 }
 }

在上面 JobCandidate 类被重写的 compareTo()方法中,我们实现了基于年龄的比较逻辑。我见过很多程序员将(this.getAge() – candidate.getAge())作为返回的比较结果。尽管使用这种 return 语句看上去似乎很吸引人,并且也不会对我们的例子造成影响,我的建议是远离这种语句。想象一下,比较整数值,其中有一个或者两个都是负数的结果。这会导致一些错误,让你的程序行为不定,而且更重要的是,这样的错误是很细微的,尤其是在大型的企业应用中很难检测出来。下面我们将写一个辅助类,为委托方对包含了 JobCandidate 元素的 ArrayList 对象进行排序。
JobCandidateSorter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
packageguru.springframework.blog.sortarraylist.comparable;
importjava.util.ArrayList;
 importjava.util.Collections;
 publicclassJobCandidateSorter {
 ArrayList jobCandidate =newArrayList<>();
 publicJobCandidateSorter(ArrayList jobCandidate) {
 this.jobCandidate = jobCandidate;
 }
 publicArrayList getSortedJobCandidateByAge() {
 Collections.sort(jobCandidate);
 returnjobCandidate;
 }
 }

在 JobCandidateSorter 类中,我们初始化了一个 ArrayList 对象,委托方将通过构造函数实例化 JobCandidateSorter 。然后我们编写了 getSortedJobCandidateByAge()方法,在这个方法中,我们调用 Collections.sort()并传递已经初始化了的 ArrayList 为参数,最后返回排序后的 ArrayList。
接下来,我们写一个测试类来测试一下我们的代码。
JobCandidateSorterTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
packageguru.springframework.blog.sortarraylist.comparable;
 importorg.junit.Test;
 importjava.lang.reflect.Array;
 importjava.util.ArrayList;
 importstaticorg.junit.Assert.*;
 publicclassJobCandidateSorterTest {
 <a href="http://www.jobbole.com/members/madao">@Test</a>
 publicvoidtestGetSortedJobCandidateByAge()throwsException {
 JobCandidate jobCandidate1 =newJobCandidate("Mark Smith","Male",26);
 JobCandidate jobCandidate2 =newJobCandidate("Sandy Hunt","Female",23);
 JobCandidate jobCandidate3 =newJobCandidate("Betty Clark","Female",20);
 JobCandidate jobCandidate4 =newJobCandidate("Andrew Styne","Male",24);
 ArrayList jobCandidateList =newArrayList&lt;&gt;();
 jobCandidateList.add(jobCandidate1);
 jobCandidateList.add(jobCandidate2);
 jobCandidateList.add(jobCandidate3);
 jobCandidateList.add(jobCandidate4);
 JobCandidateSorter jobCandidateSorter =newJobCandidateSorter(jobCandidateList);
 ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByAge();
 System.out.println("-----Sorted JobCandidate by age: Ascending-----");
for(JobCandidate jobCandidate : sortedJobCandidate) {
 System.out.println(jobCandidate);
 }
 }
 }

在上面的测试类中,我们创建了四个 JobCandidate 对象并把它们添加到 ArrayList,然后传递这个 ArrayList 到构造函数来实例化 JobCandidateSorter 类。最后,我们调用 JobCandidateSorter 类的 getSortedJobCandidateByAge()方法,并打印这个方法返回的排序后的 ArrayList。测试的输出结果如下:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running guru.springframework.blog.sortarraylist.comparable.JobCandidateSorterTest
-----Sorted JobCandidate by age: Ascending-----
Name: Betty Clark, Gender: Female, age:20
Name: Sandy Hunt, Gender: Female, age:23
Name: Andrew Styne, Gender: Male, age:24
Name: Mark Smith, Gender: Male, age:26
Tests run:1, Failures:0, Errors:0, Skipped:0, Time elapsed:0.003sec
- in guru.springframework.blog.sortarraylist.comparable.JobCandidateSorterTest

使用 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
packageguru.springframework.blog.sortarraylist.comparator;
 importjava.util.Comparator;
 publicclassJobCandidate {
 privateString name;
 privateString gender;
 privateintage;
 publicJobCandidate(String name, String gender,intage) {
 this.name = name;
 this.gender = gender;
 this.age = age;
 }
 publicString getName() {
 returnname;
 }
 publicString getGender() {
 returngender;
 }
 publicintgetAge() {
 returnage;
 }
 publicstaticComparator ageComparator =newComparator() {
 @Override
 publicintcompare(JobCandidate jc1, JobCandidate jc2) {
 return(jc2.getAge() < jc1.getAge() ? -1:
 (jc2.getAge() == jc1.getAge() ?0:1));
 }
 };
 publicstaticComparator nameComparator =newComparator() {
 @Override
 publicintcompare(JobCandidate jc1, JobCandidate jc2) {
 return(int) (jc1.getName().compareTo(jc2.getName()));
 }
 };
 @Override
 publicString toString() {
 return" Name: "+this.name +", Gender: "+this.gender +", age:"+this.age;
 }
 }

在上面的类中,从 29 行到 35 行,我们写了一个匿名类并实现了 compare()方法,按照年龄的降序对 JobCandidate 对象进行排序。从37行到42行,我们又写了一个匿名类并实现了 compare() 方法,按照姓名的升序对 JobCandidate进行排序。现在我们写一个类,为委托方对 ArrayList 的元素进行排序。
JobCandidateSorter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
packageguru.springframework.blog.sortarraylist.comparator;
 importjava.util.ArrayList;
 importjava.util.Collections;
 publicclassJobCandidateSorter {
 ArrayList jobCandidate =newArrayList<>();
 publicJobCandidateSorter(ArrayList jobCandidate) {
 this.jobCandidate = jobCandidate;
 }
 publicArrayList getSortedJobCandidateByAge() {
 Collections.sort(jobCandidate, JobCandidate.ageComparator);
 returnjobCandidate;
 }
 publicArrayList getSortedJobCandidateByName() {
 Collections.sort(jobCandidate, JobCandidate.nameComparator);
 returnjobCandidate;
 }
 }

在上面的类中,我们写了 getSortedJobCandidateByAge()方法,在这个方法内部我们调用了 Collections.sort()的重载版本,这个版本传递要被排序的 ArrayList 对象和比较年龄的 Comparator 对象。在 getSortedJobCandidateByName()方法内部,我们又调用了 Collections.sort()的另一个重载版本,这个版本传递要被排序的 ArrayList 对象和比较姓名的 Comparator 对象。
Let’s write a test class to test our code.
让我们写一个测试类来测试我们的代码。
JobCandidateSorterTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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 {
 JobCandidateSorter jobCandidateSorter;
 @Before
 publicvoidsetUp()throwsException {
 JobCandidate jobCandidate1 =newJobCandidate("Mark Smith","Male",26);
 JobCandidate jobCandidate2 =newJobCandidate("Sandy Hunt","Female",23);
 JobCandidate jobCandidate3 =newJobCandidate("Betty Clark","Female",20);
 JobCandidate jobCandidate4 =newJobCandidate("Andrew Styne","Male",24);
 ArrayList jobCandidateList =newArrayList&lt;&gt;();
 jobCandidateList.add(jobCandidate1);
 jobCandidateList.add(jobCandidate2);
 jobCandidateList.add(jobCandidate3);
 jobCandidateList.add(jobCandidate4);
 jobCandidateSorter =newJobCandidateSorter(jobCandidateList);
 }
 <a href="http://www.jobbole.com/members/madao">@Test</a>
 publicvoidtestGetSortedJobCandidateByAge()throwsException {
 System.out.println("-----Sorted JobCandidate by age: Descending-----");
ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByAge();
 for(JobCandidate jobCandidate : sortedJobCandidate) {
 System.out.println(jobCandidate);
 }
 }
 <a href="http://www.jobbole.com/members/madao">@Test</a>
 publicvoidtestGetSortedJobCandidateByName()throwsException {
 System.out.println("-----Sorted JobCandidate by name: Ascending-----");
ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByName();
 for(JobCandidate jobCandidate : sortedJobCandidate) {
 System.out.println(jobCandidate);
 }
 }
 }

在测试类中我们向 ArrayList 中添加若干 JobCandidate 对象,并使用 Before 注释在测试单元的 setup()方法中创建了一个 JobCandidateSorter 对象。如果你是一个 Junit 新手,可以参考我以前的文章包括 Junit 注释(Junit 单元测试系列)。在 testGetSortedJobCandidateByAge()测试方法中我们调用了 getSortedJobCandidateByAge()方法,并打印了该方法返回的排序后的 ArrayList。在 testGetSortedJobCandidateByName()测试方法中我们调用了getSortedJobCandidateByName()方法并同样打印该方法返回的 ArrayList。测试的输出如下:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running guru.springframework.blog.sortarraylist.comparator.JobCandidateSorterTest
-----Sorted JobCandidate by name: Ascending-----
Name: Andrew Styne, Gender: Male, age:24
 Name: Betty Clark, Gender: Female, age:20
 Name: Mark Smith, Gender: Male, age:26
 Name: Sandy Hunt, Gender: Female, age:23
-----Sorted JobCandidate by age: Descending-----
Name: Mark Smith, Gender: Male, age:26
 Name: Andrew Styne, Gender: Male, age:24
 Name: Sandy Hunt, Gender: Female, age:23
 Name: Betty Clark, Gender: Female, age:20
 Tests run:2, Failures:0, Errors:0, Skipped:0, Time elapsed:0.023sec
- in guru.springframework.blog.sortarraylist.comparator.JobCandidateSorterTest

总结

在本文中我们看到了 ArrayList 排序的不同方法。一种是使用 Comparable 另一种是使用 Comparator。方法的选择一直是造成程序员们困惑的原因之一。你最应该记住的就是一个 Comparable 对象可以说“我可以自己与另外一个对象比较”而一个 Comparator 对象可以说“我可以比较两个不同的对象”。你不能说一个接口比另一个要好。选择的接口取决于你需要实现的功能。

TAGS:Java List排序

猜你喜欢

本月热门的内容

最近更新的内容

NewHot