java基础的运算相关的基本都是涉及到基本类型的数值类型的计算,比如>,<,+,-
等,但是如果是对象的比较应该如何进行呢?
比如我们京东上买东西,挑选商品时候需要对商品价格进行排序。万事万物皆对象,所以引出我们的java比较器。
在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。Java中的对象,正常情况下,只能进行比较: ==或!=(也就是两个对象的引用地址是否相同),不能使用>或<来比较对象的大小。但是在实际的开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小,如何实现?使用两个接口中的任何一个: Comparable或 Comparator
。
Java实现对象排序的方式有两种:
自然排序:java.lang.Comparable
定制排序:java.util.Comparator
Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。如果当前对象this大于形参对象obj,则返回正整数,如果当前对象this小于形参对象obj,则返回负整数,如果当前对象this等于形参对象obj,则返回零。
Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些 类是可以和自己比较的。若一个类实现了Comparable接口,就意味着该类支持排序。
实现Comparable接口的对象列表(比如list集合)和数组可以通过 Collections.sort 或Arrays.sort进行自动排序( Collections或Arrays的排序的参数分别适用于集合和数组,牢记)。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparable 的典型实现:(默认都是从小到大排列的)
String:按照字符串中字符的Unicode值进行比较
Character:按照字符的Unicode值来进行比较
数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
Date、Time等:后面的日期时间比前面的日期时间大
public class CompareTest { @Test public void test01(){ String[] arr = {"AA","CC","KK","DD","JJ","MM","BB","GG","DQ"}; Arrays.sort(arr); System.out.println(Arrays.toString(arr)); } @Test public void test02(){ Goods[] goodsArr = new Goods[4]; goodsArr[0] = new Goods("lenovoMouse",45); goodsArr[1] = new Goods("dellMouse",34); goodsArr[2] = new Goods("xiaomiMouse",12); goodsArr[3] = new Goods("huaweiMouse",89); Arrays.sort(goodsArr); System.out.println(goodsArr.toString()); } @Test public void test03(){ List<Goods> list = new ArrayList<>(); Goods goods0 = new Goods("lenovoMouse",45); Goods goods1 = new Goods("dellMouse",34); Goods goods2 = new Goods("xiaomiMouse",12); Goods goods3 = new Goods("huaweiMouse",89); list.add(goods0); list.add(goods1); list.add(goods2); list.add(goods3); Collections.sort(list); System.out.println(list.toString()); } } class Goods implements Comparable{ private String goodsName; private double price; public Goods() { } public Goods(String goodsName, double price) { this.goodsName = goodsName; this.price = price; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Goods{" + "goodsName='" + goodsName + '\'' + ", price=" + price + '}'; } @Override public int compareTo(Object o) { if(o instanceof Goods){ Goods goods = (Goods) o; if (this.price > ((Goods) o).price){ return 1; }else if (this.price < ((Goods) o).price ){ return -1; } } return 0; } }
疑问:什么时候选择定制排序Comparator?
Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。
个人认为有两种情况可以使用实现Comparator接口的方式:
可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。
还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
package com.ethan; import org.junit.Test; import java.util.*; public class CompareTest { @Test public void test04(){ Goods[] goodsArr = new Goods[4]; goodsArr[0] = new Goods("lenovoMouse",45); goodsArr[1] = new Goods("dellMouse",34); goodsArr[2] = new Goods("xiaomiMouse",12); goodsArr[3] = new Goods("huaweiMouse",89); //重点 Arrays.sort(goodsArr, new Comparator<Goods>(){ @Override public int compare(Goods o1, Goods o2) { return -o1.compareTo(o2); } }); System.out.println(Arrays.toString(goodsArr)); } } //set/get/toString/构造函数省略 class Goods implements Comparable{ private String goodsName; private double price; @Override public int compareTo(Object o) { if(o instanceof Goods){ Goods goods = (Goods) o; if (this.price > ((Goods) o).price){ return 1; }else if (this.price < ((Goods) o).price ){ return -1; } } return 0; } }
package com.ethan; import java.io.BufferedReader; import java.io.FileReader; import java.util.*; /** * 将文件中的英文文档中的英文单词读取出来并对重复单词出现的次数进行排序 */ public class CountWords { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new FileReader("D:\\upload\\4.txt")); StringBuffer sb = new StringBuffer();//可变字符串,用来存放文档中的所有英文单词 String text =null; while ((text=br.readLine())!= null){ sb.append(text);// 将读取出的字符追加到Stringbuffer中 } br.close(); // 关闭读入流 String str = sb.toString().toLowerCase(); // 将stringBuffer转为字符并转换为小写 System.out.println(str); String[] words = str.split("[^a-zA-Z]+"); // 通过正则表达式,得到所有单词 Map<String ,Integer> map = new HashMap<String, Integer>();//因为hashmap的键值是不允许重复的,可以用来归档重复单词 for(String word :words){//遍历所有单词,将单词放在key里,value里存放单词出现次数 if(map.get(word)==null){ // 若不存在说明是第一次,则加入到map,出现次数为1 map.put(word,1); }else{ map.put(word,map.get(word)+1); // 若存在,次数累加1 } } // 这块运用了比较器排序。 List<Map.Entry<String ,Integer>> list = new ArrayList<>(map.entrySet()); Comparator<Map.Entry<String,Integer>> comparator = new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> left, Map.Entry<String, Integer> right) { return (left.getValue().compareTo(right.getValue())); } }; // 集合默认升序排列 Collections.sort(list,comparator); for(int i=0;i<list.size();i++){// 由高到低输出 if(i>99){ break; } System.out.println(list.get(list.size()-i-1).getKey() +":"+list.get(list.size()-i-1).getValue()); } } }