好用的java.util.Objects

在jdk1.7中,新增了一个工具类,就是java.util.Objects类。它有3个简单的封装方法,对于平常的使用来说挺有用的,分别是:hashCodeequalstoString这3个方法。

1、hashcode生成

// 1. Objects.hash(Object... values)
public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

// 2. Arrays.hashCode(Object a[])
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;
}

// 3. Object.hashCode()
public native int hashCode();

首先,Objects的hash方法接收可变参数,可变参数的内部是一个数组。然后内部调用Arrays的hashCode方法,我们来看一下其方法:核心是遍历每一个参数来计算result值,在计算的过程中,每一个参数上转型为Object使用hashCode来生成随机值。那么Object的hashCode又是什么?这是一个本地方法,源码不给出实现,其数值和对象的内存地址有关。

参数虽然上转型为Object,但是对于String、Integer等对象类型,它们都重写了hashCode方法。

因为是基于内存地址的生成,所以不同对象生成的hashcode值冲突的几率是很小的。

2、对象equals比较

// 1. Objects.equals(Object a, Object b)
public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

// 2. Object.equals(Object obj)
public boolean equals(Object obj) {
  return (this == obj);
}

首先是对象的引用判断,其次就是调用对象自身的equals方法来比较,如果对象类型没有重写equals的话,就会调用Object的equals方法(仍然是对象的引用比较)。

事实上,我们进行equals比较的对象,除了引用对象之外,就是String、Integer(自动装箱后)等类型了。

对于String,直接进行引用比较。而对于Integer、Double等对象类型,它们都重写了equals方法,贴出Integer的equals方法:

public boolean equals(Object obj) {
  if (obj instanceof Integer) {
      return value == ((Integer)obj).intValue();
  }
    return false;
}

可以看到,内部是进行值相等判断。

hashCode与equals方法的重要性

对于散列结构(hash)的集合类型,比如说HashMap、HashSet等,如果我们用它们来存储我们自定义的对象,那么我们就必要重写类的hashCode与equals方法。<u>为什么要重写,这个原因就不赘述了。</u> 还有Set集合类,由于它存储不重复的元素。

下面我们给出利用Objects工具类的一种较为简单实用的重写方式:

public class Building {

    private String name;
    private double area;

    @Override
    public int hashCode() {
        return Objects.hash(name, area);
    }

    @Override
    public boolean equals(Object obj) {
          // 引用相等判断
        if (obj == this) {
            return true;
        }
          // 类型判断
        if (!(obj instanceof Building)) {
            return false;
        }
        Building building = (Building) obj;
          // 关键域相等判断
        // 基本类型不要用用Objects.equals()方法,会自动装箱带来额外开销
        return Objects.equals(name, building.name) && (area == building.area);
    }

    @Override
    public String toString() {
        return Objects.toString("name = " + name, "name is undefined") + ", "
                + Objects.toString("area = " + area, "area is undefined");
    }

      // setter、getter
}

3、toString

我们自己重写toString方法时,有一点比较难写,就是默认值设置。

// Objects.toString(Object o, String nullDefault)
public static String toString(Object o, String nullDefault) {
    return (o != null) ? o.toString() : nullDefault;
}

// Objects.toStirng(Object 0)
public static String toString(Object o) {
  return String.valueOf(o);
}

我们在重写toString时,可以参考上面的Building类的写法。