EffecitveJava 第二章

来源:互联网 发布:手机上头像源码怎么用 编辑:程序博客网 时间:2024/05/17 22:53

本章主要介绍equals 和hashCode toString 这几个方法

Object 是所有类的父类,他的所以方法都有明确的通用阅读,因为他被设计成是要被覆盖的 所有 我们需要对它所提供的几个方法 需要一些了解

equals
我们什么时候需要去覆盖这个方法 ,当类有自己的特有的逻辑的时候例如

public static void main(String[] args) {        Integer s1=new Integer(1);        Integer s2=new Integer(1);        System.out.print(s1==s2?true:false);        System.out.print(s1.equals(s2)?true:false);    }我们至少需要比较两个是数值是否相同,但是 我们并不需要比较它们是否是指向同一个对象 equals 的作用处理我们手动调用这个方法还有别的地方默认会使用么? public static void main(String[] args) {        Integer s1=new Integer(1);        Integer s2=new Integer(1);        List<Integer> list=new ArrayList<>();        list.add(s1);        System.out.print(list.contains(s2));    }我们深入查看集合它们内部是怎么判断 是否有包含这是ArrayList 重写了List contain的方法 public boolean contains(Object o) {        return indexOf(o) >= 0;    }  public int indexOf(Object o) {        if (o == null) {            for (int i = 0; i < size; i++)                if (elementData[i]==null)                    return i;        } else {            for (int i = 0; i < size; i++)                if (o.equals(elementData[i]))                    return i;        }        return -1;    }对于集合的判断还是判断equals 

equals 说要尊重的几种规则也就是等价关系

1 自反性。对于任何非null 的引用值x,x.equals(x)必须要返回true
2 对称性。对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true.
3 传递性。对于任何非null的引用值x、y和z 如果x.equals(y),y.equals(z),都是返回true ,x.equals(z)必须要返回true
4 一致性 任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y),必须返回true或者必须返回false
对于任何非null 的引用值 x x.equals(null) 必须返回false

以下将挑几个例子来讲上诉的重要性

对称性public final class CaseInsensitiveString {    public   final String s;    public CaseInsensitiveString(String s) {        if (s==null)            throw new NullPointerException();        this.s = s;    }    @Override    public boolean equals(Object o) {        if (o instanceof CaseInsensitiveString){            return s.equalsIgnoreCase(((CaseInsensitiveString)o).s);        }        if (o instanceof String){            return s.equalsIgnoreCase((String) o);        }        return false;    }}当前的equals切图和普通的字符串进行了比较,看上去好像并不没有什么问题,但是**String的equals 不知道区分大小写** List<CaseInsensitivieString>List=new ArrayList<CaseInsensitivieString>();list.add(cis);如果list.contain(s)会不会有问题? 为了解决这个问题直接改成下面这个样子删除和String equals的比较 @Override    public boolean equals(Object o) {        if (o instanceof CaseInsensitiveString){            return s.equalsIgnoreCase(((CaseInsensitiveString)o).s);        }        return false;    }

传递性
表现在继承的情况下 如

class Point{ private int x; private 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;    }}class ColorPoint extent Point{ private color color public Point(int x,int y,int color){   super(x,y);   this.color=color; }}如果这时候不重写 的话 有色点和无色点是分不清的 会被忽略,所以我们需要重写equals  @Override    public boolean equals(Object o) {       if(!(o instanceof Point)){        return false;       }       if(!(o instanceof ColorPoint)){        return o.equals(this);       }       return super.equals(o)&&((ColorPoint)o).color==color;    }   ColorPoint p1=new ColorPoint(1,2,RED);   Point p2=new Point(1,2);   ColorPoint p3=new ColorPOint(12,Blue);   此时p1.equals(p2)和p2.equals(p3)都是返回true 但是p1,equals(p3)却不是   解决办法暂时没有最优解

覆盖equals 总要覆盖hashCode方法
每一个覆盖equals方法的类也必须要覆盖hashCode的方法
在对象多次调用中hashcode 必须始终返回同一个整数, 如果两个对象equals不相同,那么调用两个对象的hashcode方法不一定要生成不同的整数结果。但是给出不同的的整数结果,有利于提高山列表的性能。
一个好的散列函数,通常倾向于“为不相同的对象产生不相同的散列码”
Set 存放不同的Object 以HashSet为例
HashSet 内部有一个HashMap 他的add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
是否相同并不是判断equals 而是判断hashCode,当我们确保两个对象是相等的对象的时候需要判断是否具有相等的散列码。
如果你将每个对象的 hashCode() 方法都返回相同的值,这依然可以让相应的集合正常工作,但是这会让每个对象都被映射到同一个散列桶中,使散列桶退化为链表。它使得本应该线性时间运行的线程变成了以平方级时间再运行。。
一些hasCode 返回值的解决方法

如果该域是boolean类型,则计算 f?0:1 如果该域是bytecharshort或者int类型,则计算 (int)f  如果该域是long类型,则计算 (int)(f ^ (f >>> 32))  如果该域是double类型,则计算 Double.doubleToLongBits(f)   如果该域是一个对象引用,并且该类的equals方法通过递归调用equals的方式来比较这个域,则同样对这个域递归调用hashCode。 如果该域是一个数组,则把每一个元素当做单独的域处理。也就是说,递归地应用上诉规则,对每一个重要的元素计算散列值,然后根据2.b的方法把这些散列值组合起来

toString
描述一个类 你可以返回你当前类的属性等 默认的toString 方法
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
clone
查看拷贝模式

Comparable 接口 是个比较器 用户排序关系 2>1我们知道 但 小明和小红两个对象 需要我们自己去做排序 需要实现 Comparable 这个接口也可以是生成比较器 处理 不过多介绍

0 0
原创粉丝点击