3、作为有序的关联性容器,键对象的排序机制自然是一个无法回避的问题,但幸运的是,尽管这两种容器在某些处理细节上存在一定的差异,然而整个排序机制的思路却是如出一辙,可以这样说,它们是神似而形不似,而造成“形不似”的主要原因则主要来自于这两种语言的本身。下面我们先介绍Java中TreeMap的键对象排序机制。

  1)一种常用的方式是让键对象成为Comparable<T>接口的实现类,这样TreeMap的内部则可以利用接口方法compareTo()的返回值来判断该键对象在容器中所在的位置。见如下代码:

public class MyTest implements Comparable<MyTest> {
         private int key;
         public MyTest(int key) {
             this.key = key;
         }
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof MyTest))
                 return false;
             MyTest t = (MyTest)o;
             System.out.println("equals of MyTest is called here.");
             return key == t.key;
         }
         @Override
         public int compareTo(MyTest o) {
             return key - o.key;
         }
         public static void main(String[] args) {
             TreeMap<MyTest,Integer> tm = new TreeMap<MyTest,Integer>();
             tm.put(new MyTest(5), 5);
             tm.put(new MyTest(2), 2);
             tm.put(new MyTest(10), 10);
             tm.put(new MyTest(10), 20);
             for (Entry<MyTest,Integer> e : tm.entrySet())
                 System.out.println("Key = " + e.getKey().key + ", Value = " + e.getValue());
         }
     }  
     //compareTo of MyTest is called here.
    //compareTo of MyTest is called here.
    //compareTo of MyTest is called here.
    //compareTo of MyTest is called here.
    //Key = 2, Value = 2
    //Key = 5, Value = 5
    //Key = 10, Value = 20

  从输出结果可以看出,MyTest对象在被插入到容器时,该类覆盖实现的compareTo方法被多次调用,而且终的排序结果也是和compareTo方法中的逻辑完全吻合。这里还需要额外指出的是,TreeMap和C++中的map一样,都是不允许重复键的插入,从而保证容器对象中已有键对象的性。这是如何实现的呢?在Java中,当compareTo函数返回0的时候表示两个对象是相等的,我们从上例中可以看出,通常用于JDK对象相等性比较的equals函数并没有被调用,由此可以进一步确认,键对象的相等性比较完全是由compareTo函数来决定的。在此方面,C++中的map容器亦采用了相同的设计理念,只是在细节处理方面不同且更为灵活。我们将在本条目的后面给出更具体的说明。

  2)另外一种方式是在构造TreeMap对象时传入Comparator<T>接口的实现类,而该实现类的compare方法将帮助TreeMap对象来确定如何排序容器中的键对象。见如下代码:

public class MyTest {
         private int key;
         public MyTest(int key) {
             this.key = key;
         }
         public static void main(String[] args) {
             TreeMap<MyTest,Integer> tm = new TreeMap<MyTest,Integer>(new Comparator<MyTest>() {
                 @Override
                 public int compare(MyTest first,MyTest second) {
                     System.out.println("compare of Comparator is called here.");
                     return first.key - second.key;
                 }
             });
             tm.put(new MyTest(5), 5);
             tm.put(new MyTest(2), 2);
             tm.put(new MyTest(10), 10);
             for (Entry<MyTest,Integer> e : tm.entrySet())
                 System.out.println("Key = " + e.getKey().key + ", Value = " + e.getValue());
         }
     }
     //compare of Comparator is called here.
    //compare of Comparator is called here.
    //Key = 2, Value = 2
    //Key = 5, Value = 5
    //Key = 10, Value = 10

  从上面的输出结果可以看出,Comparator匿名实现类的compare方法确实决定了TreeMap容器中键对象的排序顺序。

  现在是时候该展示一下C++中map容器的排序机制了,这里我们先看一下map的类声明形式。

  template <class Key, class Type, class Traits = less<Key>, class Allocator=allocator<pair <const Key, Type> > > class map

  其中前两个模参分别对应于键对象类型和值对象类型,这一点和Java中TreeMap是一样的。第三个模板参数则在map容器键对象的排序规则上扮演着极为重要的角色。至于后一个参数,主要用于对象的分配机制,由于和本条目关系不大,我们可以直接略过它。

  1)从该类的模参声明中可以看到STL的设计者为模参Traits提供了缺省类型参数less<Key>,该类型仍为模板类,其模参类型等同于map中的键类型。这里我们先看一下less<Key>的声明。

1     template<class _Ty>
2     struct less : public binary_function<_Ty, _Ty, bool> {
3         bool operator()(const _Ty& _Left, const _Ty& _Right) const {
4             return (_Left < _Right);
5         }
6     };