使用 == 和 equals
== :判断两个对象的地址是否相等。比较其内存地址
equlas:作用是比较两个对象是否相等,存在两种情况
  情况1:类没有覆盖重写equals方法,则使用的是父类 Object 的 equals 方法。即通过 “==” 比较两个对象内存地址。
  情况2:如果覆盖重写了equals方法,一般,比较两个对象的内容是否相等。

比如在String 类中的equals方法被重写过:

java new 相同对象对比 java比较两个对象相等_java new 相同对象对比

java new 相同对象对比 java比较两个对象相等_数据_02

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

String中hashCode()

关于自定义类中比较对象对象是否相等的实现:
--- 首先先了解什么是hashcode,作用 ---
  hashcode就是散列码,使用高效率的哈希算法来定位查找对象。
比如 String str = "abcd",那么计算机会先计算器散列码,再将其放入到对应的内存数组索引处。
在Object类中的 toString 方法:

return getClass().getName() + “@” + Integer.toHexString(hashCode());

在String类中的hashCode算法

public int hashCode() {
    int h = hash;  //private int hash; // Default to 0
    if (h == 0 && value.length > 0) {
        char val[] = value;  //value是存储的字符数组

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

数学表达式:

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

  s[ i ] 即String 的第 i 个字符,n 是 str 的长度。
上述表达式的计算过程:

String str = "abcd";
 
h = 0
value.length = 4
 
val[0] = a
val[1] = b
val[2] = c
val[3] = d
 
h = 31*0 + a
  = a
 
h = 31 * (31*0 + a) + b
  = 31 * a + b
 
h = 31 * (31 * (31*0 + a) + b) + c
  = 31 * (31 * a + b) + c
  = 31 * 31 * a + 31 * b + c
 
h = 31 * (31 * 31 * a + 31 * b + c) + d
  = 31 * 31 * 31 * a + 31 * 31 * b + 31 * c + d
 
h = 31 ^ (n-1) * val[0] + 31 ^ (n-2) * val[1] + 31 ^ (n-3) * val[2] + ...+ val[n-1]

  -->虽然 字符串长度太长,int类型可能丢失数据,但是依然可以起到散列的作用。

为什么使用 31 作为散列码,而不是其他的数字?
计算机的乘法会涉及到移位计算,选择 31 的原因是一个素数。

质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数

在存储数据,计算hash地址的时候,我们希望能尽量较少同样的 hash 地址,即“hash冲突”。
如果得到的相同的 hash 地址过多的话,那么这些数据组成的hash链(链表)就会更长,从而会降低查询的效率。
所以在选择系数的时候选择尽量长的(31=11111 [2] )系数且乘法尽量不要溢出(大于31 的话,很容易溢出)的系数。
如果计算出来的Hash 地址越大,那么冲突就会越小,查找的效率也会提高。

31 可以由 i*31 == (i<<5)-i 表示,使用31 是为了更好地分配hash (int类型) 地址。并且31 只占用了 5 bits。
Integer 类的最大值是  : 2147483647
在java乘法中如果数字相乘过大,会导致移除的问题,导致数据的丢失。

--- Objects 类中的 hash() 方法实现 ---

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}
    |
    | Arrays类的hashCode() 方法
    V
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;
}
    |
    | Object的hashCode() 方法
    V
public native int hashCode();

  -->Object 类中的 hashCode 方法,是本地方法,也就是C语言实现。该方法将对象的内存地址转换成 int 类型整数返回。

--- 关于hashCode 和equals 方法的重写 ---

public class Person {    
    private String name;
    private int age;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

 

java new 相同对象对比 java比较两个对象相等_java new 相同对象对比

java new 相同对象对比 java比较两个对象相等_数据_02

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
    |
    |
    V
 public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
    |
    |
    V
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
    |
    |
    V
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

HashSet保证唯一无序

--- 为什么要有hashCode ---
在将对象加入到HashSet 的时候,在HashMap 中会先计算对象的 hashcode 值来判断对象加入的位置。
如果没有相等的 hashcode 则表示该对象没有添加过。进行添加。
如果存在星等的 hashcode(由于哈希碰撞),则使用 equals 方法检查两个对象是否相同的。
  如果相同的话就不会进行加入。
  如果相同,就会重新散列到其他位置。