Java ArrayList 的不同排序方法

简介:

由于其功能性和灵活性,ArrayList是 Java 集合框架中使用最为普遍的集合类之一。ArrayList 是一种 List 实现,它的内部用一个动态数组来存储元素,因此 ArrayList 能够在添加和移除元素的时候进行动态的扩展和缩减。你可能已经使用过 ArrayList,因此我将略过基础部分。如果你对 ArrayList 还不熟悉,你可以参考它的 API 文档,可以很容易理解在 ArrayList 上执行基本的操作。

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

排序字符串对象的 ArrayList

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

SortArrayListAscendingDescending.java

 
  1. package guru.springframework.blog.sortarraylist.ascendingdescending; 
  2. import java.util.ArrayList; 
  3. import java.util.Collections; 
  4. public class SortArrayListAscendingDescending { 
  5. private ArrayList arrayList; 
  6. public SortArrayListAscendingDescending(ArrayList arrayList) { 
  7. this.arrayList = arrayList; 
  8. public ArrayList getArrayList() { 
  9. return this.arrayList; 
  10. public ArrayList sortAscending() { 
  11. Collections.sort(this.arrayList); 
  12. return this.arrayList; 
  13. public ArrayList sortDescending() { 
  14. Collections.sort(this.arrayList, Collections.reverseOrder()); 
  15. return this.arrayList; 

在上面的类中,我们在构造器中初始化了一个 ArrayList 对象。在 sortAscending()方法中,我们调用了 Collections.sort()方法,并传递这个初始化的 ArrayList对象为参数,返回排序后的 ArrayList。在 sortDescending()方法中,我们调用重载的 Collections.sort()方法让其按照降序对元素排序,这个版本的 Collections.sort()接收ArrayList对象作为第一个参数,一个由 Collections.reverseOrder()方法返回的 Comparator 对象作为第二个参数。我们将会在稍后讲解 Comparator。为了测试排序功能,我们将写一段测试代码。

SortArrayListAscendingDescendingTest.java

 
  1. package guru.springframework.blog.sortarraylist.ascendingdescending; 
  2. import org.junit.Test; 
  3. import java.util.ArrayList; 
  4. import static org.junit.Assert.*; 
  5. public class SortArrayListAscendingDescendingTest { 
  6. <a href="http://www.jobbole.com/members/madao">@Test</a> 
  7. public void testSortAscendingDescending() throws Exception { 
  8. ArrayList countryList = new ArrayList&lt;&gt;(); 
  9. countryList.add("France"); 
  10. countryList.add("USA"); 
  11. countryList.add("India"); 
  12. countryList.add("Spain"); 
  13. countryList.add("England"); 
  14. SortArrayListAscendingDescending sortArrayList = new SortArrayListAscendingDescending(countryList); 
  15. ArrayList unsortedArrayList = sortArrayList.getArrayList(); 
  16. System.out.println("Unsorted ArrayList: " + unsortedArrayList); 
  17. ArrayList sortedArrayListAscending = sortArrayList.sortAscending(); 
  18. System.out.println("Sorted ArrayList in Ascending Order : " + sortedArrayListAscending); 
  19. ArrayList sortedArrayListDescending = sortArrayList.sortDescending(); 
  20. System.out.println("Sorted ArrayList in Descending Order: " + sortedArrayListDescending); 

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

输出如下:

 
  1. ------------------------------------------------------- 
  2. T E S T S 
  3. -------------------------------------------------------   
  4. Running guru.springframework.blog.sortarraylist.ascendingdescending.SortArrayListAscendingDescendingTest   
  5. Unsorted ArrayList: [France, USA, India, Spain, England] 
  6. Sorted ArrayList in Ascending Order : [England, France, India, Spain, USA] 
  7. Sorted ArrayList in Descending Order: [USA, Spain, India, France, England]   
  8. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec - 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()方法。

JobCandidate类的代码如下:

JobCandidate.java

 
  1. package guru.springframework.blog.sortarraylist.comparable; 
  2. public class JobCandidate implements Comparable { 
  3. private String name; 
  4. private String gender; 
  5. private int age; 
  6. public JobCandidate(String name, String gender, int age) { 
  7. this.name = name; 
  8. this.gender = gender; 
  9. this.age = age; 
  10. public String getName() { 
  11. return name; 
  12. public String getGender() { 
  13. return gender; 
  14. public int getAge() { 
  15. return age; 
  16. @Override 
  17. public int compareTo(JobCandidate candidate) { 
  18. return (this.getAge() < candidate.getAge() ? -1 : 
  19. (this.getAge() == candidate.getAge() ? 0 : 1)); 
  20. @Override 
  21. public String toString() { 
  22. return " Name: " + this.name + ", Gender: " + this.gender + ", age:" + this.age; 

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

JobCandidateSorter.java

 
  1. package guru.springframework.blog.sortarraylist.comparable; 
  2. import java.util.ArrayList; 
  3. import java.util.Collections; 
  4. public class JobCandidateSorter { 
  5. ArrayList jobCandidate = new ArrayList<>(); 
  6. public JobCandidateSorter(ArrayList jobCandidate) { 
  7. this.jobCandidate = jobCandidate; 
  8. public ArrayList getSortedJobCandidateByAge() { 
  9. Collections.sort(jobCandidate); 
  10. return jobCandidate; 

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

接下来,我们写一个测试类来测试一下我们的代码。

JobCandidateSorterTest.java

 
  1. package guru.springframework.blog.sortarraylist.comparable; 
  2. import org.junit.Test; 
  3. import java.lang.reflect.Array; 
  4. import java.util.ArrayList; 
  5. import static org.junit.Assert.*; 
  6. public class JobCandidateSorterTest { 
  7. <a href="http://www.jobbole.com/members/madao">@Test</a> 
  8. public void testGetSortedJobCandidateByAge() throws Exception { 
  9. JobCandidate jobCandidate1 = new JobCandidate("Mark Smith""Male"26); 
  10. JobCandidate jobCandidate2 = new JobCandidate("Sandy Hunt""Female"23); 
  11. JobCandidate jobCandidate3 = new JobCandidate("Betty Clark""Female"20); 
  12. JobCandidate jobCandidate4 = new JobCandidate("Andrew Styne""Male"24); 
  13. ArrayList jobCandidateList = new ArrayList&lt;&gt;(); 
  14. jobCandidateList.add(jobCandidate1); 
  15. jobCandidateList.add(jobCandidate2); 
  16. jobCandidateList.add(jobCandidate3); 
  17. jobCandidateList.add(jobCandidate4); 
  18. JobCandidateSorter jobCandidateSorter = new JobCandidateSorter(jobCandidateList); 
  19. ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByAge(); 
  20. System.out.println("-----Sorted JobCandidate by age: Ascending-----"); 
  21. for (JobCandidate jobCandidate : sortedJobCandidate) { 
  22. System.out.println(jobCandidate); 

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

 
  1. ------------------------------------------------------- 
  2. T E S T S 
  3. ------------------------------------------------------- 
  4. Running guru.springframework.blog.sortarraylist.comparable.JobCandidateSorterTest 
  5. -----Sorted JobCandidate by age: Ascending----- 
  6. Name: Betty Clark, Gender: Female, age:20 
  7. Name: Sandy Hunt, Gender: Female, age:23 
  8. Name: Andrew Styne, Gender: Male, age:24 
  9. Name: Mark Smith, Gender: Male, age:26 
  10. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.003 sec 
  11. - 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. package guru.springframework.blog.sortarraylist.comparator; 
  2. import java.util.Comparator; 
  3. public class JobCandidate { 
  4. private String name; 
  5. private String gender; 
  6. private int age; 
  7. public JobCandidate(String name, String gender, int age) { 
  8. this.name = name; 
  9. this.gender = gender; 
  10. this.age = age; 
  11. public String getName() { 
  12. return name; 
  13. public String getGender() { 
  14. return gender; 
  15. public int getAge() { 
  16. return age; 
  17. public static Comparator ageComparator = new Comparator() { 
  18. @Override 
  19. public int compare(JobCandidate jc1, JobCandidate jc2) { 
  20. return (jc2.getAge() < jc1.getAge() ? -1 : 
  21. (jc2.getAge() == jc1.getAge() ? 0 : 1)); 
  22. }; 
  23. public static Comparator nameComparator = new Comparator() { 
  24. @Override 
  25. public int compare(JobCandidate jc1, JobCandidate jc2) { 
  26. return (int) (jc1.getName().compareTo(jc2.getName())); 
  27. }; 
  28. @Override 
  29. public String toString() { 
  30. return " Name: " + this.name + ", Gender: " + this.gender + ", age:" + this.age; 

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

JobCandidateSorter.java

 
  1. package guru.springframework.blog.sortarraylist.comparator; 
  2. import java.util.ArrayList; 
  3. import java.util.Collections; 
  4. public class JobCandidateSorter { 
  5. ArrayList jobCandidate = new ArrayList<>(); 
  6. public JobCandidateSorter(ArrayList jobCandidate) { 
  7. this.jobCandidate = jobCandidate; 
  8. public ArrayList getSortedJobCandidateByAge() { 
  9. Collections.sort(jobCandidate, JobCandidate.ageComparator); 
  10. return jobCandidate; 
  11. public ArrayList getSortedJobCandidateByName() { 
  12. Collections.sort(jobCandidate, JobCandidate.nameComparator); 
  13. return jobCandidate; 

在上面的类中,我们写了 getSortedJobCandidateByAge()方法,在这个方法内部我们调用了 Collections.sort()的重载版本,这个版本传递要被排序的 ArrayList 对象和比较年龄的 Comparator 对象。在 getSortedJobCandidateByName()方法内部,我们又调用了 Collections.sort()的另一个重载版本,这个版本传递要被排序的 ArrayList 对象和比较姓名的 Comparator 对象。

让我们写一个测试类来测试我们的代码。

JobCandidateSorterTest.java

 
  1. package guru.springframework.blog.sortarraylist.comparator; 
  2. import guru.springframework.blog.sortarraylist.comparator.JobCandidate; 
  3. import guru.springframework.blog.sortarraylist.comparator.JobCandidateSorter; 
  4. import org.junit.Before; 
  5. import org.junit.Test; 
  6. import java.util.ArrayList; 
  7. import static org.junit.Assert.*; 
  8. public class JobCandidateSorterTest { 
  9. JobCandidateSorter jobCandidateSorter; 
  10. @Before 
  11. public void setUp() throws Exception { 
  12. JobCandidate jobCandidate1 = new JobCandidate("Mark Smith""Male"26); 
  13. JobCandidate jobCandidate2 = new JobCandidate("Sandy Hunt""Female"23); 
  14. JobCandidate jobCandidate3 = new JobCandidate("Betty Clark""Female"20); 
  15. JobCandidate jobCandidate4 = new JobCandidate("Andrew Styne""Male"24); 
  16. ArrayList jobCandidateList = new ArrayList&lt;&gt;(); 
  17. jobCandidateList.add(jobCandidate1); 
  18. jobCandidateList.add(jobCandidate2); 
  19. jobCandidateList.add(jobCandidate3); 
  20. jobCandidateList.add(jobCandidate4); 
  21. jobCandidateSorter = new JobCandidateSorter(jobCandidateList); 
  22. <a href="http://www.jobbole.com/members/madao">@Test</a> 
  23. public void testGetSortedJobCandidateByAge() throws Exception { 
  24. System.out.println("-----Sorted JobCandidate by age: Descending-----"); 
  25. ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByAge(); 
  26. for (JobCandidate jobCandidate : sortedJobCandidate) { 
  27. System.out.println(jobCandidate); 
  28. <a href="http://www.jobbole.com/members/madao">@Test</a> 
  29. public void testGetSortedJobCandidateByName() throws Exception { 
  30. System.out.println("-----Sorted JobCandidate by name: Ascending-----"); 
  31. ArrayList sortedJobCandidate = jobCandidateSorter.getSortedJobCandidateByName(); 
  32. for (JobCandidate jobCandidate : sortedJobCandidate) { 
  33. System.out.println(jobCandidate); 

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

 
  1. ------------------------------------------------------- 
  2. T E S T S 
  3. ------------------------------------------------------- 
  4. Running guru.springframework.blog.sortarraylist.comparator.JobCandidateSorterTest 
  5. -----Sorted JobCandidate by name: Ascending----- 
  6. Name: Andrew Styne, Gender: Male, age:24 
  7. Name: Betty Clark, Gender: Female, age:20 
  8. Name: Mark Smith, Gender: Male, age:26 
  9. Name: Sandy Hunt, Gender: Female, age:23 
  10. -----Sorted JobCandidate by age: Descending----- 
  11. Name: Mark Smith, Gender: Male, age:26 
  12. Name: Andrew Styne, Gender: Male, age:24 
  13. Name: Sandy Hunt, Gender: Female, age:23 
  14. Name: Betty Clark, Gender: Female, age:20 
  15. Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 sec 
  16. - in guru.springframework.blog.sortarraylist.comparator.JobCandidateSorterTest 

总结

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


来源:51CTO

相关文章
|
11月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
620 18
|
11月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
487 4
|
算法 Java 开发者
Java 项目实战数字华容道与石头迷阵游戏开发详解及实战方法
本文介绍了使用Java实现数字华容道和石头迷阵游戏的技术方案与应用实例,涵盖GUI界面设计、二维数组操作、游戏逻辑控制及自动解法算法(如A*),适合Java开发者学习游戏开发技巧。
786 46
|
12月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
308 11
|
11月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
426 4
|
11月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
571 5
|
12月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
缓存 Java 开发者
Java 开发者必看!ArrayList 和 LinkedList 的性能厮杀:选错一次,代码慢成蜗牛
本文深入解析了 Java 中 ArrayList 和 LinkedList 的性能差异,揭示了它们在不同操作下的表现。通过对比随机访问、插入、删除等操作的效率,指出 ArrayList 在多数场景下更高效,而 LinkedList 仅在特定情况下表现优异。文章强调选择合适容器对程序性能的重要性,并提供了实用的选择法则。
530 3
|
算法 Java
Java语言实现链表反转的方法
这种反转方法不需要使用额外的存储空间,因此空间复杂度为,它只需要遍历一次链表,所以时间复杂度为,其中为链表的长度。这使得这种反转链表的方法既高效又实用。
732 0
|
存储 Java 数据处理
Java映射操作:深入Map.getOrDefault与MapUtils方法
结合 `getOrDefault`方法的简洁性及 `MapUtils`的丰富功能,Java的映射操作变得既灵活又高效。合理地使用这些工具能够显著提高数据处理的速度和质量。开发人员可以根据具体的应用场景选择适宜的方法,以求在性能和可读性之间找到最佳平衡。
406 0