Google Guava之常见Object方法


文中所述Guava版本基于29.0-jre,文中涉及到的代码完整示例请移步Github查看。

常见Obejct方法使用

Java中所有的类都有一个隐藏的公共父类,就是Object类。既然所有的类都继承自Object类,那所有的类中都包含有Object类的方法,常见的有。

equals(Object):boolean
hashCode():int
toString():String
notify():void
notifyAll():void
wait():void
wait()long:void
wait(long, int):void

Object都提供了上述方法的默认实现,但是某些情况下默认的实现不能满足我们的需求,此时就需要一些强大健全的三方实现来满足我们,Guava中提供的一些Object类常见方法便可以满足我们。

equals

由于所有类都默认继承Object,同时继承了equals方法,所以我们在比较对象是否相等的时候可以直接使用
A.equals(B)来比较(A为null会有异常,B为null可以正常比较结果为false)。由于对null类型使用equals方式会抛出异常,导致我们在每次使用之前都会进行null判断if (null != A) {}。使用Guava的Objects.equal(Object, Object):boolean可以免于我们对对象是否为null的判定,直接使用即可。

Objects.equal(A, A); 
Objects.equal(null, A); 
Objects.equal(A, null);
Objects.equal(null, null);
/*Output:
true
false
false
true
///:~

注:JDK7引入的Obejcts类中的Obejcts.equals(Object, Object):boolan提供同样的功能。

hashCode

计算对象的hash值是我们在日常编程中经常需要进行的步骤,因为大量使用了对象容器类如Map、Set等,这些对象容器类判定对象冲突时需要依赖对象的hash值。而且我们有时还要费心思的设计一个碰撞概率比较小的hash算法,保证对象hash值足够分散,同时对拥有相同值的对象计算出相同的hash值。Guava的Objects.hashCode(Object...):int提供计算对象hash值的一般方法,保证在大多数情况下满足我们的需求。

public class Person {

    private String name;
    private Integer age;

    // 省略getter setter和构造函数
    
    @Override
    public int hashCode() {
        return Objects.hashCode(this.name, this.age);
    }
    
    public static void main(String[] args) {
        Person alice = new Person("alice", 18);
        System.out.println(alice.hashCode());
    }
}
/*Output:
-1414972077
///:~

hash方法的实现如下

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

对象是null时hash值为0,值为null则该值的hash值为0,其余的值直接调用JDK的native方法进行hash值的计算(若是想深入了解可以查询hashCode()方法在native的实现)。
注:JDK7引入的Obejcts类中的Obejcts.hash(Object...):int提供同样的功能。

toString

toString方法帮助我们更加详细的打印对象的内部信息,但是默认的实现对于复杂对象直接打印的是对象的内存地址,使用Guava的MoreObjects.toStringHelper可以轻松编写有用的toString方法。

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
            .add("name", name)
            .add("age", age)
            .toString();
}
/*Output:
Person{name=alice, age=18}
///:~

对于使用Intellij IDEA的同学来说,则可以直接借助IDEA提供的快捷功能快速生成toString方法。

compare/compareTo

Java中对象的比较一般有两种办法,一是继承Comparable接口并实现compareTo(T o)方法,另外一种是创建Comparator类。Guava提供了一种在compareTo(T o)方法中快速比较对象的功能ComparisonChain

public class Person implements Comparable<Person> {

    private String name;
    private Integer age;
    
    // 省略getter setter和构造函数

    @Override
    public int compareTo(Person other) {
        return ComparisonChain.start()
                .compare(this.name, other.name)
                .compare(this.age, other.age, Ordering.natural().nullsLast())
                .result();
    }
}

可以看到,ComparisonChainstart方法开启比较,中间是多个compare方法,最后调用result返回结果,而且在使用compare的时候,可以使用Guava提供的Ordering来实现更多的功能(关于Guava Ordering的介绍请移步Google Guava之Ordering)。
ComparisonChain执行一种懒比较:它执行比较操作直至发现非零的结果,在那之后的比较输入将被忽略。这句话可能不是那么好理解,我们结合上面的示例来分析这句话。

return ComparisonChain.start()                      \\ 1
    .compare(this.name, other.name)                 \\ 2
    .compare(this.age, other.age, Ordering.natural().nullsLast()) \\ 3
    .result();                                      \\ 4

1处代码开启比较,如果在2处代码比较后结果非零(即this.name小于other.name或this.name大于other.name),3处的代码即忽略执行,直接在4处得到结果。

Guava是如何实现这种懒比较呢?Guava实现此项功能使用了100多行的代码,下面我们来分析这100多行的代码。

public abstract class ComparisonChain {
    // 调用start方法返回ACTIVE
    private static final ComparisonChain ACTIVE = new ComparisonChain() {
        public ComparisonChain compare(Comparable left, Comparable right) {
            return this.classify(left.compareTo(right));
        }

        public <T> ComparisonChain compare(@Nullable T left, @Nullable T right, Comparator<T> comparator) {
            return this.classify(comparator.compare(left, right));
        }

        public ComparisonChain compare(int left, int right) {
            return this.classify(Ints.compare(left, right));
        }

        public ComparisonChain compare(long left, long right) {
            return this.classify(Longs.compare(left, right));
        }

        public ComparisonChain compare(float left, float right) {
            return this.classify(Float.compare(left, right));
        }

        public ComparisonChain compare(double left, double right) {
            return this.classify(Double.compare(left, right));
        }

        public ComparisonChain compareTrueFirst(boolean left, boolean right) {
            return this.classify(Booleans.compare(right, left));
        }

        public ComparisonChain compareFalseFirst(boolean left, boolean right) {
            return this.classify(Booleans.compare(left, right));
        }

        // 所有的compare方法最终都要调用classify方法,classify方法接收的是比较后的值,比较结果为0则返回ComparisonChain.ACTIVE,否则依据结果返回ComparisonChain.LESS或ComparisonChain.GREATER,ComparisonChain.LESS和ComparisonChain.GREATER都是ComparisonChain的子类InactiveComparisonChain实例
        
        ComparisonChain classify(int result) {
            return result < 0 ? ComparisonChain.LESS : (result > 0 ? ComparisonChain.GREATER : ComparisonChain.ACTIVE);
        }

        public int result() {
            return 0;
        }
    };
    
    // LESS使用-1作为构造函数的参数,表示对LESS调用result返回-1
    private static final ComparisonChain LESS = new ComparisonChain.InactiveComparisonChain(-1);
    // GREATER使用1作为构造函数的参数,表示对GREATER调用result返回1
    private static final ComparisonChain GREATER = new ComparisonChain.InactiveComparisonChain(1);

    private ComparisonChain() {
    }

    public static ComparisonChain start() {
        return ACTIVE;
    }

    public abstract ComparisonChain compare(Comparable<?> var1, Comparable<?> var2);

    public abstract <T> ComparisonChain compare(@Nullable T var1, @Nullable T var2, Comparator<T> var3);

    public abstract ComparisonChain compare(int var1, int var2);

    public abstract ComparisonChain compare(long var1, long var3);

    public abstract ComparisonChain compare(float var1, float var2);

    public abstract ComparisonChain compare(double var1, double var3);

    /** @deprecated */
    @Deprecated
    public final ComparisonChain compare(Boolean left, Boolean right) {
        return this.compareFalseFirst(left, right);
    }

    public abstract ComparisonChain compareTrueFirst(boolean var1, boolean var2);

    public abstract ComparisonChain compareFalseFirst(boolean var1, boolean var2);

    public abstract int result();

    private static final class InactiveComparisonChain extends ComparisonChain {
        final int result;

        InactiveComparisonChain(int result) {
            super(null);
            this.result = result;
        }

        //  一旦内部状态由ACTIVE转为InactiveComparisonChain,后续再调用compare都不再执行真正的比较操作,直接忽略
        
        
        public ComparisonChain compare(@Nullable Comparable left, @Nullable Comparable right) {
            return this;
        }

        public <T> ComparisonChain compare(@Nullable T left, @Nullable T right, @Nullable Comparator<T> comparator) {
            return this;
        }

        public ComparisonChain compare(int left, int right) {
            return this;
        }

        public ComparisonChain compare(long left, long right) {
            return this;
        }

        public ComparisonChain compare(float left, float right) {
            return this;
        }

        public ComparisonChain compare(double left, double right) {
            return this;
        }

        public ComparisonChain compareTrueFirst(boolean left, boolean right) {
            return this;
        }

        public ComparisonChain compareFalseFirst(boolean left, boolean right) {
            return this;
        }

        public int result() {
            return this.result;
        }
    }
}

ComparisonChain内部有一个实例ACTIVE,和一个子类InactiveComparisonChain,子类InactiveComparisonChain有两个实例LESSGREATER,比较的过程就是三个实例之间的转变ACTIVE->LESS或者ACTIVE->GREATER

  1. ComparisonChain.start()调用后返回实例ACTIVE
  2. 调用compare方法,结果为0则返回新的实例ACTIVE,结果小于0返回实例LESS,结果大于0返回实例GREATER
  3. 若是步骤2返回的是ACTIVE则继续步骤2过程,若返回是LESSGREATER则再调用compare方法则不进行比较,直接忽略并返回LESSGREATER
  4. 最后调用result返回结果,ACTIVE返回0,LESS返回-1,GREATER返回1

参考

posted @ 2020-07-17 15:14  weegee  阅读(348)  评论(0编辑  收藏  举报