我喜欢这种遨游在Java的世界里,精心研究学习新鲜事物的感受,即使再小再细再微不足道的东西,也让我乐此不疲,同时我也更愿意将我所会的东西分享出来供你们学习以及方便本身往后回顾。好了,闲话很少说,今天主要是介绍Comparable和Comparator俩个接口(参照JavaAPI)以及差别,而且经过一些示例进行说明。html
此接口强行对实现它的每一个类的对象进行总体排序。这种排序被称为类的天然排序,类的 compareTo 方法被称为它的天然比较方法。java
实现此接口的对象列表(和数组)能够经过 Collections.sort
(和 Arrays.sort
)进行自动排序。实现此接口的对象能够用做有序映射(如TreeMap)中的键或有序集合(TreeSet)中的元素,无需指定比较器。sql
对于类 C 的每个 e1 和 e2 来讲,当且仅当 e1.compareTo(e2) == 0 与 e1.equals(e2) 具备相同的 boolean 值时,类 C 的天然排序才叫作与 equals 一致。注意,null 不是任何类的实例,即便 e.equals(null) 返回 false,e.compareTo(null) 也将抛出 NullPointerException。api
建议(虽然不是必需的)最好使天然排序与 equals 一致。这是由于在使用天然排序与 equals 不一致的元素(或键)时,没有显式比较器的有序集合(和有序映射表)行为表现“怪异”。尤为是,这样的有序集合(或有序映射表)违背了根据 equals 方法定义的集合(或映射表)的常规协定。数组
例如,若是将两个键 a 和 b 添加到没有使用显式比较器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那么第二个 add 操做将返回 false(有序集合的大小没有增长),由于从有序集合的角度来看,a 和 b 是相等的。数据结构
实际上,全部实现 Comparable 的 Java 核心类都具备与 equals 一致的天然排序。java.math.BigDecimal 是个例外,它的天然排序将值相等但精确度不一样的BigDecimal 对象(好比 4.0 和 4.00)视为相等。app
从数学上讲,定义给定类 C 上天然排序的关系式 以下:ide
{(x, y)|x.compareTo(y) <= 0}。
总体排序的 商 是:函数
{(x, y)|x.compareTo(y) == 0}。
它直接遵循 compareTo 的协定,商是 C 的 等价关系,天然排序是 C 的 总体排序。当说到类的天然排序 与 equals 一致 时,是指天然排序的商是由类的equals(Object)
方法定义的等价关系。学习
{(x, y)|x.equals(y)}。
此接口是 Java Collections Framework 的成员。
Authenticator.RequestorType, BigDecimal, BigInteger, Boolean, Byte, ByteBuffer, Calendar, Character, CharBuffer, Charset,ClientInfoStatus, CollationKey, Component.BaselineResizeBehavior, CompositeName, CompoundName, Date, Date, Desktop.Action,Diagnostic.Kind, Dialog.ModalExclusionType, Dialog.ModalityType, Double, DoubleBuffer, DropMode, ElementKind, ElementType, Enum,File, Float, FloatBuffer, Formatter.BigDecimalLayoutForm, FormSubmitEvent.MethodType, GregorianCalendar, GroupLayout.Alignment,IntBuffer, Integer, JavaFileObject.Kind, JTable.PrintMode, KeyRep.Type, LayoutStyle.ComponentPlacement, LdapName, Long, LongBuffer,MappedByteBuffer, MemoryType, MessageContext.Scope, Modifier, MultipleGradientPaint.ColorSpaceType,MultipleGradientPaint.CycleMethod, NestingKind, Normalizer.Form, ObjectName, ObjectStreamField, Proxy.Type, Rdn,Resource.AuthenticationType, RetentionPolicy, RoundingMode, RowFilter.ComparisonType, RowIdLifetime, RowSorterEvent.Type,Service.Mode, Short, ShortBuffer, SOAPBinding.ParameterStyle, SOAPBinding.Style, SOAPBinding.Use, SortOrder, SourceVersion,SSLEngineResult.HandshakeStatus, SSLEngineResult.Status, StandardLocation, String, SwingWorker.StateValue, Thread.State, Time,Timestamp, TimeUnit, TrayIcon.MessageType, TypeKind, URI, UUID, WebParam.Mode, XmlAccessOrder, XmlAccessType, XmlNsForm
Delayed, Name, RunnableScheduledFuture<V>, ScheduledFuture<V>
该接口只有一个方法:
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
说明:
假设咱们经过 x.compareTo(y) 来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。
强行对某个对象 collection 进行总体排序的比较函数。能够将 Comparator 传递给 sort 方法(如 Collections.sort
或 Arrays.sort
),从而容许在排序顺序上实现精确控制。还可使用 Comparator 来控制某些数据结构(如有序 set
或有序映射
)的顺序,或者为那些没有天然顺序
的对象 collection 提供排序。
当且仅当对于一组元素 S 中的每一个 e1 和 e2 而言,c.compare(e1, e2)==0 与 e1.equals(e2) 具备相等的布尔值时,Comparator c 强行对 S 进行的排序才叫作与 equals 一致 的排序。
当使用具备与 equals 不一致的强行排序能力的 Comparator 对有序 set(或有序映射)进行排序时,应该当心谨慎。假定一个带显式 Comparator c 的有序 set(或有序映射)与从 set S 中抽取出来的元素(或键)一块儿使用。若是 c 强行对 S 进行的排序是与 equals 不一致的,那么有序 set(或有序映射)将是行为“怪异的”。尤为是有序 set(或有序映射)将违背根据 equals 所定义的 set(或映射)的常规协定。
例如,假定使用 Comparator c
将知足 (a.equals(b) && c.compare(a, b) != 0)
的两个元素 a
和 b
添加到一个空 TreeSet
中,则第二个 add
操做将返回 true(树 set 的大小将会增长),由于从树 set 的角度来看,a
和 b
是不相等的,即便这与 Set.add
方法的规范相反。
注:一般来讲,让 Comparator 也实现 java.io.Serializable 是一个好主意,由于它们在可序列化的数据结构(像 TreeSet
、TreeMap
)中可用做排序方法。为了成功地序列化数据结构,Comparator(若是已提供)必须实现 Serializable。
在算术上,定义给定 Comparator c 对给定对象 set S 实施强行排序 的关系式 为:
{(x, y) such that c.compare(x, y) <= 0}.
此总体排序的 商 (quotient) 为:
{(x, y) such that c.compare(x, y) == 0}.
它直接遵循 compare 的协定,商是 S 上的 等价关系,强行排序是 S 上的 总体排序。当咱们说 c 强行对 S 的排序是 与 equals 一致 的时,意思是说排序的商是对象的 equals(Object)
方法所定义的等价关系:
{(x, y) such that x.equals(y)}.
此接口是 Java Collections Framework 的成员。
该接口有俩个方法:
package java.util; public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
说明:
(1) 若一个类要实现Comparator接口:它必定要实现compareTo(T o1, T o2) 函数,但能够不实现 equals(Object obj) 函数。
由于任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;因此,其它全部的类也至关于都实现了该函数。
(2) int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。
Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;咱们若须要控制某个类的次序,能够创建一个“该类的比较器”来进行排序。
咱们不难发现:Comparable至关于“内部比较器”,而Comparator至关于“外部比较器”。
Comparable接口测试代码:
public class Book implements Comparable { // 定义名为Book的类,默认继承自Object类 public int id;// 编号 public String name;// 名称 public double price; // 价格 private String author;// 做者 public GregorianCalendar calendar;// 出版日期 public Book() { this(0, "X", 0.0, new GregorianCalendar(), ""); } public Book(int id, String name, double price, GregorianCalendar calender, String author) { this.id = id; this.name = name; this.price = price; this.calendar = calender; this.author = author; } // 重写继承自父类Object的方法,知足Book类信息描述的要求 public String toString() { String showStr = id + "\t" + name; // 定义显示类信息的字符串 DecimalFormat formatPrice = new DecimalFormat("0.00");// 格式化价格到小数点后两位 showStr += "\t" + formatPrice.format(price);// 格式化价格 showStr += "\t" + author; SimpleDateFormat formatDate = new SimpleDateFormat("yyyy年MM月dd日"); showStr += "\t" + formatDate.format(calendar.getTime()); // 格式化时间 return showStr; // 返回类信息字符串 } public int compareTo(Object obj) {// Comparable接口中的方法 Book b = (Book) obj; return (int) (this.price - b.price); // 按书的id比较大小,用于默认排序 } public static void main(String[] args) { Book b1 = new Book(1, "红楼梦", 130.86, new GregorianCalendar(2009, 01, 25), "曹雪芹、高鄂"); Book b2 = new Book(2, "三国演义", 99.99, new GregorianCalendar(2008, 7, 8), "罗贯中 "); Book b3 = new Book(3, "水浒传", 80.3, new GregorianCalendar(2009, 6, 28), "施耐庵 "); Book b4 = new Book(4, "西游记", 111.8, new GregorianCalendar(2011, 6, 8), "吴承恩"); Book b5 = new Book(5, "天龙八部", 8.4, new GregorianCalendar(2011, 9, 23), "金庸"); TreeMap<Object,Integer> tm = new TreeMap<Object, Integer>(); tm.put(b1, new Integer(2550)); tm.put(b2, new Integer(1220)); tm.put(b3, new Integer(324)); tm.put(b4, new Integer(453)); tm.put(b5, new Integer(40)); Iterator it = tm.keySet().iterator(); Object key = null, value = null; Book bb = null; while (it.hasNext()) { key = it.next(); bb = (Book) key; value = tm.get(key); System.out.println(bb.toString() + "\t库存:" + tm.get(key)); } } }
代码讲解:
建立了一个book实体类,继承了Comparable,此时它将能够进行排序。
public class Book implements Comparable { . . . }
建立一个带参构造器,用于排序使用。
public Book(int id, String name, double price, GregorianCalendar calender, String author) { this.id = id; this.name = name; this.price = price; this.calendar = calender; this.author = author; }
重写继承自父类Object的方法,知足Book类信息描述的要求 .
public String toString() { String showStr = id + "\t" + name; // 定义显示类信息的字符串 DecimalFormat formatPrice = new DecimalFormat("0.00");// 格式化价格到小数点后两位 showStr += "\t" + formatPrice.format(price);// 格式化价格 showStr += "\t" + author; SimpleDateFormat formatDate = new SimpleDateFormat("yyyy年MM月dd日"); showStr += "\t" + formatDate.format(calendar.getTime()); // 格式化时间 return showStr; // 返回类信息字符串 }
Comparable接口中的方法,按书的价格asc排序(排序呢方式能够自定义)
public int compareTo(Object obj) {// Comparable接口中的方法 Book b = (Book) obj; return (int) (this.price - b.price); // 按书的价格比较大小,用于默认排序 }
在main函数中使用treeMap对其默认排序。
Book b1 = new Book(10000, "红楼梦", 150.86, new GregorianCalendar(2009, 01, 25), "曹雪芹、高鄂"); Book b2 = new Book(10001, "三国演义", 99.68, new GregorianCalendar(2008, 7, 8), "罗贯中 "); Book b3 = new Book(10002, "水浒传", 100.8, new GregorianCalendar(2009, 6, 28), "施耐庵 "); Book b4 = new Book(10003, "西游记", 120.8, new GregorianCalendar(2011, 6, 8), "吴承恩"); Book b5 = new Book(10004, "天龙八部", 10.4, new GregorianCalendar(2011, 9, 23), "搜狐"); TreeMap<Object,Integer> tm = new TreeMap<Object, Integer>(); tm.put(b1, new Integer(255)); tm.put(b2, new Integer(122)); tm.put(b3, new Integer(688)); tm.put(b4, new Integer(453)); tm.put(b5, new Integer(40)); Iterator it = tm.keySet().iterator(); Object key = null, value = null; Book bb = null; while (it.hasNext()) { key = it.next(); bb = (Book) key; value = tm.get(key); System.out.println(bb.toString() + "\t库存:" + tm.get(key)); }
最终运行获得结果为:
10004 天龙八部 10.40 搜狐 2011年10月23日 库存:40 10001 三国演义 99.68 罗贯中 2008年08月08日 库存:122 10002 水浒传 100.80 施耐庵 2009年07月28日 库存:688 10003 西游记 120.80 吴承恩 2011年07月08日 库存:453 10000 红楼梦 150.86 曹雪芹、高鄂 2009年02月25日 库存:255
Comparator测试代码:
/** * * UseComparator * @author LongJin * 2017年2月28日 下午2:26:45 */ public class UseComparator { public static void main(String args[]) { List<Book> list = new ArrayList<Book>(); // 数组序列 Book b1 = new Book(10000, "红楼梦", 150.86, new GregorianCalendar(2009, 01, 25), "曹雪芹、高鄂"); Book b2 = new Book(10001, "三国演义", 99.68, new GregorianCalendar(2008, 7, 8), "罗贯中 "); Book b3 = new Book(10002, "水浒传", 100.8, new GregorianCalendar(2009, 6, 28), "施耐庵 "); Book b4 = new Book(10003, "西游记", 120.8, new GregorianCalendar(2011, 6, 8), "吴承恩"); Book b5 = new Book(10004, "天龙八部", 10.4, new GregorianCalendar(2011, 9, 23), "搜狐"); list.add(b1); list.add(b2); list.add(b3); list.add(b4); list.add(b5); // Collections.sort(list); //没有默认比较器,不能排序 System.out.println("数组序列中的元素:"); myprint(list); Collections.sort(list, new PriceComparator()); // 根据价格排序 System.out.println("按书的价格排序:"); myprint(list); Collections.sort(list, new CalendarComparator()); // 根据时间排序 System.out.println("按书的出版时间排序:"); myprint(list); } // 自定义方法:分行打印输出list中的元素 public static void myprint(List<Book> list) { Iterator it = list.iterator(); // 获得迭代器,用于遍历list中的全部元素 while (it.hasNext()) {// 若是迭代器中有元素,则返回true System.out.println("\t" + it.next());// 显示该元素 } } // 自定义比较器:按书的价格排序 升序 static class PriceComparator implements Comparator<Book> { @Override public int compare(Book o1, Book o2) { // TODO Auto-generated method stub return new Double(o1.price).compareTo(new Double(o2.price)); } } // 自定义比较器:按书出版时间来排序 降序 static class CalendarComparator implements Comparator<Book> { @Override public int compare(Book o1, Book o2) { // TODO Auto-generated method stub return o2.calendar.compareTo(o1.calendar); } } }
自定义比较器按书的价格升序:
// 自定义比较器:按书的价格排序 static class PriceComparator implements Comparator<Book> { @Override public int compare(Book o1, Book o2) { // TODO Auto-generated method stub return new Double(o1.price).compareTo(new Double(o2.price)); } }
自定义比较器按出版时间降序:
// 自定义比较器:按书出版时间来排序 static class CalendarComparator implements Comparator<Book> { @Override public int compare(Book o1, Book o2) { // TODO Auto-generated method stub return o2.calendar.compareTo(o1.calendar); } }
在main中添加book的list数组:
List<Book> list = new ArrayList<Book>(); // 数组序列 Book b1 = new Book(10000, "红楼梦", 150.86, new GregorianCalendar(2009, 01, 25), "曹雪芹、高鄂"); Book b2 = new Book(10001, "三国演义", 99.68, new GregorianCalendar(2008, 7, 8), "罗贯中 "); Book b3 = new Book(10002, "水浒传", 100.8, new GregorianCalendar(2009, 6, 28), "施耐庵 "); Book b4 = new Book(10003, "西游记", 120.8, new GregorianCalendar(2011, 6, 8), "吴承恩"); Book b5 = new Book(10004, "天龙八部", 10.4, new GregorianCalendar(2011, 9, 23), "搜狐"); // 添加对象到ArrayList中 list.add(b1); list.add(b2); list.add(b3); list.add(b4); list.add(b5);
自定义方法:分行打印输出list中的元素 :
public static void myprint(List<Book> list) { Iterator it = list.iterator(); // 获得迭代器,用于遍历list中的全部元素 while (it.hasNext()) {// 若是迭代器中有元素,则返回true System.out.println("\t" + it.next());// 显示该元素 } }
最后将list数组以不一样的比较器进行排序:
//Collections.sort(list); //没有默认比较器,不能排序 但这里的BOok类实现了Comparable接口,因此会以默认比较器排序 System.out.println("数组序列中的元素:"); myprint(list); Collections.sort(list, new PriceComparator()); // 根据价格排序 System.out.println("按书的价格排序:"); myprint(list); Collections.sort(list, new CalendarComparator()); // 根据时间排序 System.out.println("按书的出版时间排序:"); myprint(list);
运行代码,得出结果:
数组序列中的元素: 10000 红楼梦 150.86 曹雪芹、高鄂 2009年02月25日 10001 三国演义 99.68 罗贯中 2008年08月08日 10002 水浒传 100.80 施耐庵 2009年07月28日 10003 西游记 120.80 吴承恩 2011年07月08日 10004 天龙八部 10.40 搜狐 2011年10月23日 按书的价格排序: 10004 天龙八部 10.40 搜狐 2011年10月23日 10001 三国演义 99.68 罗贯中 2008年08月08日 10002 水浒传 100.80 施耐庵 2009年07月28日 10003 西游记 120.80 吴承恩 2011年07月08日 10000 红楼梦 150.86 曹雪芹、高鄂 2009年02月25日 按书的出版时间排序: 10004 天龙八部 10.40 搜狐 2011年10月23日 10003 西游记 120.80 吴承恩 2011年07月08日 10002 水浒传 100.80 施耐庵 2009年07月28日 10000 红楼梦 150.86 曹雪芹、高鄂 2009年02月25日 10001 三国演义 99.68 罗贯中 2008年08月08日