zoukankan      html  css  js  c++  java
  • Comparable与Comparator

    Comparable是数据与算法结合,表现为数据绑定固有的排序算法。算法只能有一种。而Comparator可以数据与算法分离,算法可以有多种实现。

    准确的讲二个接口都是实现的比较规则,都是根据业务逻辑指定比较规则。在集合或数组中使用sort方法才会达到排序的目的。

    排序的目的是为了快速过滤,比如public SortedSet<E> headSet(E toElement)

    使用哪些属性定义比较规则,使用哪些属性构造hashcode, equal灵活决定,并不是所有的属性都用来定义到这些方法里。比较规则返回的相对值而equal是is, identify的比较。

    相对值应用还比如在cronjob的语法,就支持绝对时间与相对时间来执行任务。

    一。比较

    使用Comparable接口完成排序:实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键有序集合中的元素无需指定比较器

    使用Comparator接口实现排序:实现其compare方法,根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数来判断大小。强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序(不使用天然的Comparable定义的比较规则得到排序的结果,而使用另一种排序算法得到结果),或者为那些没有自然顺序的对象 collection 提供排序(最初的类并没有实现Comparable接口,而之后又不想改变类的定义而实现排序,此时可以使用Comparator)。

    Comparable目的是让集合内部存在一个默认的比较规则,Comparator是在集合外部定义比较规则。一般我们写的bean都要实现这一接口,这也是标准javabean的规范。

    Comparator可以看成一种算法的实现,将算法和数据分离,Comparator也可以在下面两种环境下使用:
         1、类的设计师没有考虑到比较问题而没有实现Comparable,可以通过Comparator来实现排序而不必改变对象本身
         2、可以使用多种排序标准,比如升序、降序等。

    二。Comparable示例:

        private static class TaskTimmer implements Comparable<TaskTimmer>
        {
            private Date   start;
            private String taskName;
    
            public TaskTimmer(Date start, String taskName)
            {
                this.start = start;
                this.taskName = taskName;
            }
    
            public Date getStart()
            {
                return start;
            }
    
            public void setStart(Date start)
            {
                this.start = start;
            }
    
            public String getTaskName()
            {
                return taskName;
            }
    
            public void setTaskName(String taskName)
            {
                this.taskName = taskName;
            }
    
         //只要taskName相同,将来都会落到同一个hash bucket. 服务于集合元素的查询,不关心date属性值
            @Override
            public int hashCode()
            {
                return taskName.hashCode();
            }
    
         //服务于集合中查询。不关心date属性值
           @Override
            public boolean equals(Object obj)
            {
                if (this == obj)
                    return true;
                if (obj == null)
                    return false;
                if (getClass() != obj.getClass())
                    return false;
                TaskTimmer other = (TaskTimmer) obj;
                if (taskName == null)
                {
                    if (other.taskName != null)
                        return false;
                }
                else if (!taskName.equals(other.taskName))
                    return false;
                return true;
            }
    
         //只关心date值,时间越早TreeSet里越靠前
            @Override
            public int compareTo(TaskTimmer o)
            {
                if (this == o)
                    return 0;
                if (o == null)
                    return -1;
                if (o.taskName != null && this.taskName != null && this.taskName.equals(o.taskName))
                {
                    return 0;
                }
                if (this.start == null || o.getStart() == null)
                {
                    return -1;
                }
                else
                {
                    return this.start.compareTo(o.getStart());
                }
            }
        }

    针对集合的操作:

    private TreeSet<TaskTimmer>        taskTimmers;
    ......
    synchronized
    (taskTimmers) {                    //删除需要用到hashcode,equal
        taskTimmers.remove(
    new TaskTimmer(null, key)); }

           Calendar now = Calendar.getInstance(); now.add(Calendar.MINUTE, -30); Date halfHourAgo = now.getTime(); synchronized (taskTimmers) {     
    //过滤使用headSet(CompareElement),将TreeSet中存活超过半个小时的TaskTimer取出并删除。过滤用到比较规则compareTo方法。
    Set
    <TaskTimmer> deleteTasks = taskTimmers.headSet(new TaskTimmer(halfHourAgo, null)); for (TaskTimmer taskTimmer : deleteTasks) { ..... } taskTimmers.removeAll(deleteTasks); }

    注意,使用哪些属性定义比较规则,使用哪些属性构造hashcode, equal灵活决定,并不是所有的属性都用来定义到这些方法里。

    三。Comparator示例

    package com.gaoyibo.example.gold_example.treeset;
    
    import java.util.Comparator;
    import java.util.Iterator;
    import java.util.Set;
    import java.util.TreeSet;
    
    /*
     * TreeMap实现 红黑树http://baike.baidu.com/view/133754.html
     */
    public class TestTreeSet
    {
    
        public TestTreeSet()
        {
            super();
            // TODO Auto-generated constructor stub
        }
    
        /**
         * @param args
         */
        public static void main(String[] args)
        {
            //
            System.out.println("a".compareTo("b"));
            System.out.println(-("a".compareToIgnoreCase("b")));
          /*
         * Comparator可以在TreeSet的构造方法中指定,这样表示每添加一个元素就会自动排序。
         * *另一种方式是先不考虑排序,只往里添加元素,最后使用Collections.sort(ts,new InstanceComparator());
         */
         TreeSet<String> ts = new TreeSet<String>(new InstanceComparator());
            ts.add("aa");
            ts.add("BB");
            ts.add("bb"); // BB和bb被认为是相同的元素,因为BB首先输入,所以打印的是bb
            ts.add("DD");
    
            //排序的结果是dd<bb<aa. aa从最小的最元素变成最大的元素。排到set的最后。
            System.out.println(ts.first());
            System.out.println(ts.last());
    
            loadSet(ts);
    
            if (ts.contains("bb"))
            {
                System.out.println("Contains bb/BB");
            }
    
            /*
             * 返回此 set 的部分视图,要求其元素严格小于 toElement。
             */
            TreeSet<String> subTs = (TreeSet) ts.headSet("ab");
            loadSet(subTs);
    
            /*
             * subTs和ts里面的元素是共享的
             */
            System.out.println("------->subTs and ts store the same objects");
            subTs.clear();
            loadSet(subTs);
            loadSet(ts);
            /*
             * 报错headSet("ab");
             * 
             * subTs.add("a");
             */
            System.out.println("------->subTs and ts store the same objects");
            subTs.add("tt");
            loadSet(ts);
            loadSet(subTs);
            ts.remove("aa");
            loadSet(ts);
            loadSet(subTs);
            
            
        }
    
        public static void loadSet(Set s)
        {
            Iterator iter = s.iterator();
    
            while (iter.hasNext())
            {
                System.out.println(iter.next());
            }
        }
    
    }
    
    class InstanceComparator implements Comparator
    {
        /*
         * 比较用来排序的两个参数。随第一个参数小于、等于或大于第二个参数而分别返回负整数、零或正整数。 实现程序必须确保对于所有的 x 和 y 而言,都存在
         * sgn(compare(x, y)) == -sgn(compare(y, x))。(这意味着当且仅当 compare(y, x) 抛出异常时
         * compare(x, y) 才必须抛出异常。)
         * 
         * 实现程序还必须确保关系是可传递的:((compare(x, y)>0) && (compare(y, z)>0)) 意味着 compare(x,
         * z)>0。
         * 
         * 最后,实现程序必须确保 compare(x, y)==0 意味着对于所有的 z 而言,都存在 sgn(compare(x,
         * z))==sgn(compare(y, z))。
         * 
         * 虽然这种情况很普遍,但并不 严格要求 (compare(x, y)==0) == (x.equals(y))。一般说来,任何违背了这一点的
         * comparator 都应该清楚地指出这一事实。推荐的语言是“注意:此 comparator 强行进行与等号一致的排序。”
         */public int compare(Object o1, Object o2)
        {
            String text1 = o1.toString();
            String text2 = o2.toString();
            return -(text1.compareToIgnoreCase(text2)); 
    //        return (text1.compareToIgnoreCase(text2));
    
        }
    
        /*
         * 
         * 比较两个comparator,和我们一个collection集合排序没有关系 因此,comp1.equals(comp2) 意味着对于每个对象引用
         * o1 和 o2 而言,都存在 sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))。
         * 两个不同的comparator!
         */
        public boolean equals(Object obj)
        {
            return this.equals(obj);
    
        }
    }

    四。引用:

    The methods do not have to give the same answers. That depends on which objects/classes you call them.
    If you are implementing your own classes which you know you want to compare at some stage, you may have them implement the Comparable interface and implement the compareTo() method accordingly.
    If you are using some classes from an API which do not implement the Comparable interface, but you still want to compare them. I.e. for sorting. You may create your own class which implements the Comparator interface and in its compare() method you implement the logic.


    Comparable interface contains a method called compareTo(obj) which takes only one argument and it compares itself with another instance or objects of the same class.
    Comparator interface contains a method called compare(obj1,obj2) which takes two arguments and it compares the value of two objects from the same or different classes.

        •    a.compareTo(b):
Comparable interface. Compares values and returns an int which tells if the values compare less than, equal, or greater than.
If your class objects have a natural order, implement the Comparable interface and define this method. All Java classes that have a natural ordering implement this (String, Double, BigInteger, ...).
        •    compare(a, b):
Comparator interface. Compares values of two objects. This is implemented as part of the Comparator<T> interface, and the typical use is to define one or more small utility classes that implement this, to pass to methods such as sort() or for use by sorting data structures such as TreeMap and TreeSet. You might want to create a Comparator object for the following:
        ◦    Multiple comparisons. To provide several different ways to sort something. For example, you might want to sort a Person class by name, ID, age, height, ... You would define a Comparator for each of these to pass to the sort() method.
        ◦    System class To provide comparison methods for classes that you have no control over. For example, you could define a Comparator for Strings that compared them by length.
        ◦    Strategy pattern To implement a Strategy pattern, which is a situation where you want to represent an algorithm as an object that you can pass as a parameter, save in a data structure, etc.

    五。Comparator中的null处理:

    不能简单的返回1或其它的值,将null的值放到最后,这样会违反一致性。通常的做法是:Handle it like null means infinitely far away. Thus comp(1234, null) == -1, comp(null, null) == 0, comp(null, 1234) == 1. With this, you get a consistent ordering. 即null值看为无穷大。

        public static class LocationPhotosComparator implements Comparator<PhotoInfo>, Serializable
        {
            private static final long serialVersionUID = 1L;
            public  static final Map<Integer, Integer> blessedIndMap       = new HashMap<Integer, Integer>();
            static
            {
                blessedIndMap.put(UserImage.S_BLESSED_GEO_PARENT,3);
                blessedIndMap.put(UserImage.S_BLESSED_GEO_ONLY,2);
                blessedIndMap.put(UserImage.S_BLESSED_PROPERTY_ONLY,1);
            }
    
            @Override
            public int compare(PhotoInfo o1, PhotoInfo o2)
            {
                
                if (o1 == null || o2 == null)
                {
                    Logging.SERVLET.error("cannot compare null elements");
                    if (o1 == null && o2 == null){
                        return 0;
                    }else{
                        return o1==null?1:-1;
                    }
                }
                int bind1 = getIndexByBlessed(o1.getBlessed());
                int bind2 = getIndexByBlessed(o2.getBlessed());
                if (bind1 == bind2)
                {
                    //recently first show
                    Date d1=o1.getPublishedDate();
                    Date d2=o2.getPublishedDate();
                    if(d1==null || d2==null){
                        Logging.SERVLET.error("cannot compare null elements");
                        if (d1 == null && d2 == null){
                            return 0;
                        }else{
                            return d1==null?1:-1;
                        }
                    }else{
                        return -(o1.getPublishedDate().compareTo(o2.getPublishedDate()));
                    }
                }
                //blessed show order 2,1,6,other
                return -(bind1 - bind2);
            }
            
            public int getIndexByBlessed(int blessed)
            {
                return blessedIndMap.get(blessed) == null ? 0 : blessedIndMap.get(blessed);
            }
        }
    
     
  • 相关阅读:
    Python 字符串处理大全.
    图形化翻译助手
    爬虫详解
    Python 模块.
    定制序列
    Python 的property的实现 .
    Python的魔法方法 .
    通过类的装饰器以及各种单例模式(修复版本)。
    是时候写一下Python装饰器了。
    %E2%80%8C的字符串问题,卡住三个小时。
  • 原文地址:https://www.cnblogs.com/highriver/p/2678745.html
Copyright © 2011-2022 走看看