Effective java笔记-对于所有对象都通用的方法

来源:互联网 发布:大学的意义知乎 编辑:程序博客网 时间:2024/04/30 04:28

对于所有对象都通用的方法

第8条 覆盖equals时请遵守通用约定

满足以下任何一个条件,可以不覆盖equals:类的每个实例本质上都是唯一的。(如Thread)不关心是否提供了“逻辑相等”的测试功能(如Random)超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。(如Set)类是私有的或包级私有的,可以确定他的equals方法永远不会被调用

当类有自己的逻辑相等的概念且父类没有实现期望的功能时要覆盖equals,且覆盖之后还可以使这个“类的值”可以作为key,一般这种类都是一些“值类”,如String,Integer。不过有一种“值类”不需要覆盖equals,就是实例受控的类,这种类只会存在一个对象,所以对象相等和值相等就是一回事了。覆盖equals方法要遵守一些规范(equals方法实现了等价关系):自反性,对称性,传递性,一致性(非null x,y只要equals的比较操作在对象中所用的信息没有被修改,则多次调用x。equals(y)返回值时一致的),x.equals(null)必需返回false(x非null)

对称性例子:
// Broken - violates symmetry! - Pages 36-37package org.effectivejava.examples.chapter03.item08;public final class CaseInsensitiveString {    private final String s;    public CaseInsensitiveString(String s) {        if (s == null)            throw new NullPointerException();        this.s = s;    }    // Broken - violates symmetry!    @Override    public boolean equals(Object o) {        if (o instanceof CaseInsensitiveString)            return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);        if (o instanceof String) // One-way interoperability!            return s.equalsIgnoreCase((String) o);        return false;    }    // This version is correct.    // @Override public boolean equals(Object o) {    // return o instanceof CaseInsensitiveString &&    // ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);    // }    public static void main(String[] args) {        CaseInsensitiveString cis = new CaseInsensitiveString("Polish");        String s = "polish";        System.out.println(cis.equals(s) + "  " + s.equals(cis));    }}

传递性例子:
// Simple immutable two-dimensional integer point class - Page 37package org.effectivejava.examples.chapter03.item08;public class Point {    private final int x;    private final int y;    public Point(int x, int y) {        this.x = x;        this.y = y;    }    @Override    public boolean equals(Object o) {        if (!(o instanceof Point))            return false;        Point p = (Point) o;        return p.x == x && p.y == y;    }    // See Item 9    @Override    public int hashCode() {        return 31 * x + y;    }}
上面代码定义了一个Point类,它们的逻辑相等条件是x,y相等,现在再定义一个ColorPoint,继承自Point:
public enum Color {    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET}// Attempting to add a value component to Point - Pages 37 - 38package org.effectivejava.examples.chapter03.item08;public class ColorPoint extends Point {    private final Color color;    public ColorPoint(int x, int y, Color color) {        super(x, y);        this.color = color;    }    // Broken - violates symmetry!    @Override    public boolean equals(Object o) {        if (!(o instanceof ColorPoint))            return false;        return super.equals(o) && ((ColorPoint) o).color == color;    }    public static void main(String[] args) {        // First equals function violates symmetry        Point p = new Point(1, 2);        ColorPoint cp = new ColorPoint(1, 2, Color.RED);        System.out.println(p.equals(cp) + " " + cp.equals(p));    }}
你会发现这样违反了对称性,改成这样,又会违反传递性
// Broken - violates transitivity!     @Override public boolean equals(Object o) {         if (!(o instanceof Point))         return false;         If o is a normal Point, do a color-blind comparison         if (!(o instanceof ColorPoint))         return o.equals(this);         o is a ColorPoint; do a full comparison         return super.equals(o) && ((ColorPoint)o).color == color;     }// Second equals function violates transitivity        ColorPoint p1 = new ColorPoint(1, 2, Color.RED);        Point p2 = new Point(1, 2);        ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);        System.out.printf("%s %s %s%n", p1.equals(p2), p2.equals(p3),                p1.equals(p3));
你可能这样实现Point的equals方法:
// Broken - violates Liskov substitution principle - Pages 39-40    // @Override public boolean equals(Object o) {    // if (o == null || o.getClass() != getClass())    // return false;    // Point p = (Point) o;    // return p.x == x && p.y == y;    // }
这样看起来还可以,但结果是不可接受的,假设我们通过不添加值组件的方式扩展了Point
// Trivial subclass of Point - doesn't add a value component - Page 39package org.effectivejava.examples.chapter03.item08;import java.util.concurrent.atomic.AtomicInteger;public class CounterPoint extends Point {    private static final AtomicInteger counter = new AtomicInteger();    public CounterPoint(int x, int y) {        super(x, y);        counter.incrementAndGet();    }    public int numberCreated() {        return counter.get();    }}
假设又有一个方法是用来判断某个Point是否是在单位圆中的整值点:
// Test program that uses CounterPoint as Pointpackage org.effectivejava.examples.chapter03.item08;import java.util.HashSet;import java.util.Set;public class CounterPointTest {    // Initialize UnitCircle to contain all Points on the unit circle    private static final Set<Point> unitCircle;    static {        unitCircle = new HashSet<Point>();        unitCircle.add(new Point(1, 0));        unitCircle.add(new Point(0, 1));        unitCircle.add(new Point(-1, 0));        unitCircle.add(new Point(0, -1));    }    public static boolean onUnitCircle(Point p) {        return unitCircle.contains(p);    }    //里氏替换法则认为一个类型的任何重要属性也将适用于其子类型,因此为该方法定义的方法也应该在子类型上运行地很好,可是:    public static void main(String[] args) {        Point p1 = new Point(1, 0);        Point p2 = new CounterPoint(1, 0);        // Prints true        System.out.println(onUnitCircle(p1));        // Should print true, but doesn't if Point uses getClass-based equals        System.out.println(onUnitCircle(p2));    }}
到这里就说明了Point不能使用基于getClass地equals方法,那这样ColorPoint又怎么办呢,也就是说如何即扩展不可实例化的(?)类,又增加值组件(ColorPoint增加了值组件,CounterPointTest没有增加),有一个权宜之计:使用组合,以下是最终的完整解决方案:
// Simple immutable two-dimensional integer point class - Page 37package org.effectivejava.examples.chapter03.item08.composition;public class Point {    private final int x;    private final int y;    public Point(int x, int y) {        this.x = x;        this.y = y;    }    @Override    public boolean equals(Object o) {        if (!(o instanceof Point))            return false;        Point p = (Point) o;        return p.x == x && p.y == y;    }    // See Item 9    @Override    public int hashCode() {        return 31 * x + y;    }}package org.effectivejava.examples.chapter03.item08.composition;public enum Color {    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET}// Adds a value component without violating the equals contract - Page 40package org.effectivejava.examples.chapter03.item08.composition;public class ColorPoint {    private final Point point;    private final Color color;    public ColorPoint(int x, int y, Color color) {        if (color == null)            throw new NullPointerException();        point = new Point(x, y);        this.color = color;    }    /**     * Returns the point-view of this color point.     */    public Point asPoint() {        return point;    }    @Override    public boolean equals(Object o) {        if (!(o instanceof ColorPoint))            return false;        ColorPoint cp = (ColorPoint) o;        return cp.point.equals(point) && cp.color.equals(color);    }    @Override    public int hashCode() {        return point.hashCode() * 33 + color.hashCode();    }}
一致性:不要使equals方法依赖于不可靠的资源。java.net.URL就依赖于URL中主机IP地址的比较,这违反了一致性,不过因为兼容性的需要无法改变这个行为。覆盖equals时总是覆盖hashCode方法;不要将equals声明中的Object对象替换为其他类型

第九条 覆盖equals时总是覆盖hashCode

覆盖equals时如果不覆盖hashCode就会导致该类无法结合所有基于散列的集合一起工作。Object规范:1.equals基于的比较信息不变则hashCode也不能变2.x.equals(y)==true,则x.hashCode()==y.hashCode()

3.x.equals(y)==false,x.hashCode()和y.hashCode()不一定不同,但要知道尽可能使得两个不相等(equals返回false)的对象的hashCode不同可提高散列表性能

package org.effectivejava.examples.chapter03.item09;// Shows the need for overriding hashcode when you override equals - Pages 45-46import java.util.HashMap;import java.util.Map;public final class PhoneNumber {    private final short areaCode;    private final short prefix;    private final short lineNumber;    public PhoneNumber(int areaCode, int prefix, int lineNumber) {        rangeCheck(areaCode, 999, "area code");        rangeCheck(prefix, 999, "prefix");        rangeCheck(lineNumber, 9999, "line number");        this.areaCode = (short) areaCode;        this.prefix = (short) prefix;        this.lineNumber = (short) lineNumber;    }    private static void rangeCheck(int arg, int max, String name) {        if (arg < 0 || arg > max)            throw new IllegalArgumentException(name + ": " + arg);    }    @Override    public boolean equals(Object o) {        if (o == this)            return true;        if (!(o instanceof PhoneNumber))            return false;        PhoneNumber pn = (PhoneNumber) o;        return pn.lineNumber == lineNumber && pn.prefix == prefix                && pn.areaCode == areaCode;    }    // Broken - no hashCode method!    // A decent hashCode method - Page 48    // @Override public int hashCode() {    // int result = 17;    // result = 31 * result + areaCode;    // result = 31 * result + prefix;    // result = 31 * result + lineNumber;    // return result;    // }    // Lazily initialized, cached hashCode - Page 49    // private volatile int hashCode; // (See Item 71)    //    // @Override public int hashCode() {    // int result = hashCode;    // if (result == 0) {    // result = 17;    // result = 31 * result + areaCode;    // result = 31 * result + prefix;    // result = 31 * result + lineNumber;    // hashCode = result;    // }    // return result;    // }    public static void main(String[] args) {        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();        m.put(new PhoneNumber(707, 867, 5309), "Jenny");        System.out.println(m.get(new PhoneNumber(707, 867, 5309)));    }}

在散列码的计算过程中,可以把冗余域排除在外(冗余域就是可以通过其他域的值计算出来的),必需排除equals比较计算中没有用到的域,否则会违反上面第二条。

第十条 始终要覆盖toString

比较简单,直接给个例子好了:
// Adding a toString method to PhoneNumber - page 52package org.effectivejava.examples.chapter03.item10;import java.util.HashMap;import java.util.Map;public final class PhoneNumber {    private final short areaCode;    private final short prefix;    private final short lineNumber;    public PhoneNumber(int areaCode, int prefix, int lineNumber) {        rangeCheck(areaCode, 999, "area code");        rangeCheck(prefix, 999, "prefix");        rangeCheck(lineNumber, 9999, "line number");        this.areaCode = (short) areaCode;        this.prefix = (short) prefix;        this.lineNumber = (short) lineNumber;    }    private static void rangeCheck(int arg, int max, String name) {        if (arg < 0 || arg > max)            throw new IllegalArgumentException(name + ": " + arg);    }    @Override    public boolean equals(Object o) {        if (o == this)            return true;        if (!(o instanceof PhoneNumber))            return false;        PhoneNumber pn = (PhoneNumber) o;        return pn.lineNumber == lineNumber && pn.prefix == prefix                && pn.areaCode == areaCode;    }    @Override    public int hashCode() {        int result = 17;        result = 31 * result + areaCode;        result = 31 * result + prefix;        result = 31 * result + lineNumber;        return result;    }    /**     * Returns the string representation of this phone number. The string     * consists of fourteen characters whose format is "(XXX) YYY-ZZZZ", where     * XXX is the area code, YYY is the prefix, and ZZZZ is the line number.     * (Each of the capital letters represents a single decimal digit.)     *      * If any of the three parts of this phone number is too small to fill up     * its field, the field is padded with leading zeros. For example, if the     * value of the line number is 123, the last four characters of the string     * representation will be "0123".     *      * Note that there is a single space separating the closing parenthesis     * after the area code from the first digit of the prefix.     */    @Override    public String toString() {        return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);    }    public static void main(String[] args) {        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();        m.put(new PhoneNumber(707, 867, 5309), "Jenny");        System.out.println(m);    }}

第十一条 谨慎地覆盖clone

Cloneable接口的目的是作为对象的一个mixin接口(见18条),表明这样的对象允许克隆。既然Cloneable并没有包含任何方法,那他到底有什么用呢:他会改变Object中受保护的clone方法,如果一个类实现了Cloneable,Object的clone方法就返回该对象的逐域拷贝,否则就会抛出CloneNotSupportedException异常,这是接口的一种极端非典型用法,不值得效仿。还有Object的clone方法是protected的,那我们要怎么用,只要子类继承,并将子类中的clone改为public就可。(我们一般会在子类的clone中调用super。clone,后面会说)Colne方法的通用约定是非常弱的:    一般:x.clone()!=x为true;x.clone().getClass()==x.getClass() 为true;x。clone().equals(x)为true;这些不是绝对的要求。        还有要求这个过程中没有调用构造器。这个约定存在几个问题:    1.不调用构造器太强硬了    2.x.clone().getClass()通常应该等于x.getClass()又太软弱了(??)在实践中,程序员会假设:如果它们扩展了一个类,并且从子类中调用了super.clone(),返回的对象将是该子类的实例。超类能够提供这种功能的唯一途径是,返回一个通过调用super.clone而得到的对象。如果clone方法返回一个由构造器创建的对象,他就得到有错误的类。如果所有的超类都遵守这条规则,那么调用super.clone最终会调用Object的clone方法,从而创建出正确类的实例。(书上这里没怎么看懂,但可以知道的是,要遵守规则:返回的对象是通过super.clone创建的,而不是自己调用构造器创建的)例子:
// Adding a clone method to PhoneNumber - page 55package org.effectivejava.examples.chapter03.item11;import java.util.HashMap;import java.util.Map;public final class PhoneNumber implements Cloneable {    private final short areaCode;    private final short prefix;    private final short lineNumber;    public PhoneNumber(int areaCode, int prefix, int lineNumber) {        rangeCheck(areaCode, 999, "area code");        rangeCheck(prefix, 999, "prefix");        rangeCheck(lineNumber, 9999, "line number");        this.areaCode = (short) areaCode;        this.prefix = (short) prefix;        this.lineNumber = (short) lineNumber;    }    private static void rangeCheck(int arg, int max, String name) {        if (arg < 0 || arg > max)            throw new IllegalArgumentException(name + ": " + arg);    }    @Override    public boolean equals(Object o) {        if (o == this)            return true;        if (!(o instanceof PhoneNumber))            return false;        PhoneNumber pn = (PhoneNumber) o;        return pn.lineNumber == lineNumber && pn.prefix == prefix                && pn.areaCode == areaCode;    }    @Override    public int hashCode() {        int result = 17;        result = 31 * result + areaCode;        result = 31 * result + prefix;        result = 31 * result + lineNumber;        return result;    }    /**     * Returns the string representation of this phone number. The string     * consists of fourteen characters whose format is "(XXX) YYY-ZZZZ", where     * XXX is the area code, YYY is the prefix, and ZZZZ is the line number.     * (Each of the capital letters represents a single decimal digit.)     *      * If any of the three parts of this phone number is too small to fill up     * its field, the field is padded with leading zeros. For example, if the     * value of the line number is 123, the last four characters of the string     * representation will be "0123".     *      * Note that there is a single space separating the closing parenthesis     * after the area code from the first digit of the prefix.     */    @Override    public String toString() {        return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);    }    @Override    public PhoneNumber clone() {        try {            return (PhoneNumber) super.clone();        } catch (CloneNotSupportedException e) {            throw new AssertionError(); // Can't happen        }    }    public static void main(String[] args) {        PhoneNumber pn = new PhoneNumber(707, 867, 5309);        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();        m.put(pn, "Jenny");        System.out.println(m.get(pn.clone()));    }}
对象中有可变的对象时clone,例子:
// A cloneable version of Stack - Pages 56-57package org.effectivejava.examples.chapter03.item11;import java.util.Arrays;public class Stack implements Cloneable {    private Object[] elements;    private int size = 0;    private static final int DEFAULT_INITIAL_CAPACITY = 16;    public Stack() {        this.elements = new Object[DEFAULT_INITIAL_CAPACITY];    }    public void push(Object e) {        ensureCapacity();        elements[size++] = e;    }    public Object pop() {        if (size == 0)            throw new EmptyStackException();        Object result = elements[--size];        elements[size] = null; // Eliminate obsolete reference        return result;    }    public boolean isEmpty() {        return size == 0;    }    @Override    public Stack clone() {        try {            Stack result = (Stack) super.clone();            result.elements = elements.clone();            return result;        } catch (CloneNotSupportedException e) {            throw new AssertionError();        }    }    // Ensure space for at least one more element.    private void ensureCapacity() {        if (elements.length == size)            elements = Arrays.copyOf(elements, 2 * size + 1);    }    // To see that clone works, call with several command line arguments    public static void main(String[] args) {        Stack stack = new Stack();        for (String arg : args)            stack.push(arg);        Stack copy = stack.clone();        while (!stack.isEmpty())            System.out.print(stack.pop() + " ");        System.out.println();        while (!copy.isEmpty())            System.out.print(copy.pop() + " ");    }}
实际上,clone方法就是另一个构造器;你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件(invariant)如果elements域是final的,上述方案就不能正常工作,因为clone方法是被禁止给elements域赋新值。这是个根本的问题:clone架构与引用可变对象的final域的正常用法是不相兼容的,除非在原始对象和克隆对象之间安全地共享此可变对象。有时候这样还要这样:
public class HashTable implements Cloneable{    private Entry[] buckets = ...;    private static class Entry{        final Object key;        Object value;        Entry next;        Entry(Object key,Object value,Entrt next){            this.key = key;            this.value=value;            this.next = next;        }        protected Object deepCopy() {            return new Entry(hash, key, value,                                  (next==null ? null : next,deepCopy()));        }    }    public HashTable clone(){        try{            HasbTable result = (HasbTable)super.clone();            result.buckets = new Entry[buckets.length];            for(int i=0;i<buckets.length;i++)                if(buckets[i]!=null)                    result.buckets[i]=buckets[i].deepCopy();            return result;        }catch(。。。){            。。。        }    }}
不过看了这段代码,到不知道为什么上面那个类不用deepCopy呢???和构造器一样,clone方法不应该在构造地过程中,调用新对象中任何非final的方法。如果clone调用了一个被覆盖的方法,那么在该翻噶发所在的子类有机会修正他在克隆对象的状态前,该方法就会被执行,这样可能导致克隆对象和原始对象之间的不一致。Object的clone方法被声明为可抛出CloneNotSupportedException,公有的clone应该省略这个声明(为了好用),如果专门为了继承而设计的类覆盖了clone方法,这个clone方法就应该声明为protected和可抛出CloneNotSupportedException,并且不实现Cloneable。

实现拷贝还可以用拷贝构造器或拷贝工厂:拷贝构造器只是一个构造器,它唯一的参数类型是包含该构造器的类,如public Yum(Yum yum);拷贝工厂是类似于拷贝构造器的静态工厂,如:public static Yum newInstance(Yum yum);拷贝构造器和拷贝工厂比clone方法有更多优势  而且使用拷贝构造器或者拷贝工厂来代替clone方法时,并没有放弃接口的功能特性,更进一步,拷贝构造器或者拷贝工厂可以带一个参数,参数类型是通过该类实现的接口。例如所有通用集合实现都提供了一个拷贝构造器,它的参数类型为Collection或Map。基于接口的拷贝构造器或拷贝工厂(更准确地叫法是转换构造器和转换工厂),允许客户选择拷贝地实现类型,而不是强迫客户接受原始地实现类型。例如:假设你有一个HashSet,并且希望把它拷贝成一个TreeSet可以使用转换构造器:new TreeSet(s).由于clone方法具有那么多缺点,有些专家级的程序员干脆从来不去覆盖clone方法,也从来不去调用它,除非拷贝数组。

第十二条 考虑实现Comparable接口

下面的程序依赖于String实现了Comparable接口,它去掉了命令行参数列表中的重复参数,并按字母顺序打印出来:
public class WordList {    public static void main(String[] args) {        Set<String> s = new TreeSet<String>();        Collections.addAll(s, args);        System.out.println(s);    }}
java平台类库中所有值类(value classes)都实现了Comparable接口规范:1.sgn(x.compareTo(y))==-sgn(y.compareTo(x))为true2.(x.compareTo(y)>0&&y.compareTo(z)>0)=>x.compareTo(z)>03.x.compareTo(y)==0=>所有z满足sgn(x.compareTo(z))==sgn(y.compareTo(z))  4.强烈建议:(x.compareTo(y)==0)==(x.equals(y)),不过这并非绝对必要,一般来说若违反这条规定,应当说明,推荐这样的说法:“注意:该类具有内在的排序功能,但是与equals不一致(inconsistent with equals)“

针对equals的的权宜之计也同样适用于compareTo方法。如果你想为一个实现了Compareable接口的类增加值组件,请不要扩展这个类,而是要编写一个不相关的类,其中包含第一个类的一个实例。然后提供一个视图(view)方法返回这个实例。这样既可以让你自由地在第二个类上实现compareTo方法,同时也允许它的客户端在必要的时候,把第二个类的实例视同第一个类的实例关于第四条建议:    如果一个类的equals和compareTo不一致,如果有一个有序集合包含这个类的元素,那这个集合就无法遵守相关集合接口(Collection,Set,Map)的通用约定,因为对于这些接口的约定是根据equals来定义的,而有序集合使用了compareTo的等同性测试,举个例子:    BigDecimal的equals和compareTo不一致,如果你将new BigDecimal("1.0")和new BigDecimal("1.00")到HashSet中,这个HashSet将包含两个元素,但如果将它们add入TreeSet中,TreeSet将只包含一个元素。

一种compareTo实现方式:
// Making PhoneNumber comparable - Pages 65-66package org.effectivejava.examples.chapter03.item12;import java.util.NavigableSet;import java.util.Random;import java.util.TreeSet;public final class PhoneNumber implements Cloneable, Comparable<PhoneNumber> {    private final short areaCode;    private final short prefix;    private final short lineNumber;    public PhoneNumber(int areaCode, int prefix, int lineNumber) {        rangeCheck(areaCode, 999, "area code");        rangeCheck(prefix, 999, "prefix");        rangeCheck(lineNumber, 9999, "line number");        this.areaCode = (short) areaCode;        this.prefix = (short) prefix;        this.lineNumber = (short) lineNumber;    }    private static void rangeCheck(int arg, int max, String name) {        if (arg < 0 || arg > max)            throw new IllegalArgumentException(name + ": " + arg);    }    @Override    public boolean equals(Object o) {        if (o == this)            return true;        if (!(o instanceof PhoneNumber))            return false;        PhoneNumber pn = (PhoneNumber) o;        return pn.lineNumber == lineNumber && pn.prefix == prefix                && pn.areaCode == areaCode;    }    @Override    public int hashCode() {        int result = 17;        result = 31 * result + areaCode;        result = 31 * result + prefix;        result = 31 * result + lineNumber;        return result;    }    /**     * Returns the string representation of this phone number. The string     * consists of fourteen characters whose format is "(XXX) YYY-ZZZZ", where     * XXX is the area code, YYY is the prefix, and ZZZZ is the line number.     * (Each of the capital letters represents a single decimal digit.)     *      * If any of the three parts of this phone number is too small to fill up     * its field, the field is padded with leading zeros. For example, if the     * value of the line number is 123, the last four characters of the string     * representation will be "0123".     *      * Note that there is a single space separating the closing parenthesis     * after the area code from the first digit of the prefix.     */    @Override    public String toString() {        return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);    }    @Override    public PhoneNumber clone() {        try {            return (PhoneNumber) super.clone();        } catch (CloneNotSupportedException e) {            throw new AssertionError(); // Can't happen        }    }    // Works fine, but can be made faster    // public int compareTo(PhoneNumber pn) {    // // Compare area codes    // if (areaCode < pn.areaCode)    // return -1;    // if (areaCode > pn.areaCode)    // return 1;    //    // // Area codes are equal, compare prefixes    // if (prefix < pn.prefix)    // return -1;    // if (prefix > pn.prefix)    // return 1;    //    // // Area codes and prefixes are equal, compare line numbers    // if (lineNumber < pn.lineNumber)    // return -1;    // if (lineNumber > pn.lineNumber)    // return 1;    //    // return 0; // All fields are equal    // }    public int compareTo(PhoneNumber pn) {        // Compare area codes        int areaCodeDiff = areaCode - pn.areaCode;        if (areaCodeDiff != 0)            return areaCodeDiff;        // Area codes are equal, compare prefixes        int prefixDiff = prefix - pn.prefix;        if (prefixDiff != 0)            return prefixDiff;        // Area codes and prefixes are equal, compare line numbers        return lineNumber - pn.lineNumber;    }    public static void main(String[] args) {        NavigableSet<PhoneNumber> s = new TreeSet<PhoneNumber>();        for (int i = 0; i < 10; i++)            s.add(randomPhoneNumber());        System.out.println(s);    }    private static final Random rnd = new Random();    private static PhoneNumber randomPhoneNumber() {        return new PhoneNumber((short) rnd.nextInt(1000),                (short) rnd.nextInt(1000), (short) rnd.nextInt(10000));    }}
0 0
原创粉丝点击