【JavaSE】Java中的数据比较-创新互联

Java中的数据比较

在Java中数据类型主要分为两类:基本数据类型和引用数据类型。

创新互联建站专注于揭东企业网站建设,响应式网站开发,商城开发。揭东网站建设公司,为揭东等地区提供建站服务。全流程按需定制开发,专业设计,全程项目跟踪,创新互联建站专业和态度为您提供的服务

1.基本数据类型如何比较?

2.基本数据类型和引用类型如何比较?

3.引用类型间如何比较?

文章目录
    • Java中的数据比较
      • 1. 基本数据类型间如何比较?
      • 2. 基本数据类型和引用类型(包装类)如何比较?
        • 2.1 小结
      • 3. 引用类型间如何比较?
        • 3.1 包装类间的比较
          • 3.1.1 小结
          • 3.1.2 实践
        • 3.2 自定义引用类型的比较
          • 3.2.1 equals方法
          • 3.2.2 compareTo方法
          • 3.2.3 compare方法
          • 3.2.4 compareTo() 和 compare()

1. 基本数据类型间如何比较?
  • 基本数据类型又细化为整型、字符型、浮点型和布尔型;

  • 基本数据类型间通过关系运算符就可以直接比较,对数值比较。

  • 不同的基本数据类型能不能比较?

    不是任意之间都能比较。基本数据类型间,除了布尔型不可以与其他类型比较,只能自己和自己比较。整型、字符型、浮点型可以相互比较(可以比较,但是比如字符和浮点比较有什么意义?合法但不合理),不同类型的比较会发生类型提升,数据类型小的会被提升到数据类型大的。字符型采用的是Unicode编码,本质还是整型。

  • 特别注意浮点型比较会存在精度问题。浮点型的计算或者类型提升都会出现精度问题。

System.out.println(0.1f == 0.1); // false

详细参考:author:平白 – 浮点数比较


2. 基本数据类型和引用类型(包装类)如何比较?

这里主要针对整型基本数据类型和其包装类的比较。

比较之前,问为什么有基本数据类型和包装类?

解释传送门 – 基本数据类型和包装类

Integer a = 100; // 自动装箱
Long b = 100L;// 自动装箱
System.out.println(a == 100);//1. true
System.out.println(b == 100);//2. true
System.out.println(a.equals(100));//3. true
System.out.println(a.compareTo(100));//4. true
System.out.println(a.equals(b));//5. false
  • 基本数据类型与包装类直接==比较时,包装类会先拆箱,然后与基本数据类型比较,比较规则和基本数据类型之间的比较规则一样。(解释1、2)

  • 包装类都已经重写了equals和compareTo方法,使用这样的方法直接和基本数据类型比较时,先将基本类型装箱然后传参,在equals方法内会先进行类型判断进而拆箱比较,compareTo方法也是的相同类型然后拿到包装类的值在进行数值比较。(解释3、4)

// Integer equals方法和compareTo方法源码
public boolean equals(Object obj) {if (obj instanceof Integer) {// 类型检查
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

public int compareTo(Integer anotherInteger) {// 类型检查
        return compare(this.value, anotherInteger.value);
    }

public static int compare(int x, int y) {return (x< y) ? -1 : ((x == y) ? 0 : 1);
    }
2.1 小结

基本数据类型和包装类比较直接使用运算符比较即可,因为包装类会自动拆箱,最终还是基本数据类型间的比较。

注意到上述基本数据类型和包装类的代码第七行,是包装类和包装类的比较,结果为false,那通过上面equals的代码可以看到,比较前会进行类型检查(检查是不是Integer或者其子类),b是Long类型,所以为false。

那包装类能否使用运算符比较?也即引用类型能否使用运算符比较?


3. 引用类型间如何比较? 3.1 包装类间的比较
public static void main(String[] args) {Integer a = 6;
        Integer b = 6;
        Integer c = new Integer(6);
        Integer d = new Integer(6);
        Integer f = 128;
        Integer g = 128;
        Long l = 6L;

        System.out.println(a == b); //1. true
        System.out.println(a.equals(b));//2. true
        System.out.println(a == c);//3. true
        System.out.println(c == d);//4. false
        System.out.println(c.equals(d));//5. true
        System.out.println(a == l);//6. 编译器红波浪警告--cannot be applied to 'java.lang.Integer', 'java.lang.Long'
        System.out.println(a.equals(l));//7. false
        System.out.println(f == g);//8. false
    }
  • 首先引用类型间使用==比较的是地址!!!
  • Integer类内有一个对象缓存区,缓存范围为-128到127,当该范围内的数据装箱时,会直接从对象缓冲区引用对象。 范围之外的数据装箱时会直接在堆区实例化Integer对象,相当于new Integer(x)。(解释行1,3,4,8)
  • 引用类型使用==比较时两边的类型要一样。(解释行6)
  • equals方法内会先进行类型判断进而拆箱比较,根据源码不是一个类型则直接返回false。(解释5,7)
  • 基本数据类型包装类中的Byte、Short、Integer、Long的高频缓存范围为-128到127;Character的高频缓存为-128到127;Float、Double没有高频缓存区。
3.1.1 小结

1、基本数据类型使用==比较的是数值;

2、引用类型间使用==比较的是地址;

3、包装类之间比较最好使用equals和compareTo方法,避免因为缓冲区而出现问题,且用着两个方法时要注意类型问题。

3.1.2 实践

单独谈论这些问题可能体会不深,一下两个练习题可以加深理解。

  1. 栈的压入、弹出序列
  2. 155. 最小栈 - 力扣(LeetCode)

3.2 自定义引用类型的比较
  • 像包装类这种是java提供的实现类以及String类等,内部都重写了equals和compareTo方法,那如果是自定义的类如何比较?既然java内部提供的类可以重写这些方法,那自定义类是不是也可以?
  • Java里面除了Object类,所有的类都是默认会继承Object父类。Object类中就提供了equals方法。既然所有的类都继承了Object类,为什么要重写equals?
3.2.1 equals方法
  • 查看Object类equals方法:
public boolean equals(Object obj) {return (this == obj);
    }
  • Object类中的equals方法最终还是==比较,也即比较地址。这不满足其他类的需求所以被重写。
  • equals方法只能判断相等不相等!

Integer的equals方法:

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

自定义引用类型equals方法:

  1. 继承equals方法
class Student {int id;
    int name;

    public Student(int id, int name) {this.id = id;
        this.name = name;
    }

}

public class Test {public static void main(String[] args) {Student student1 = new Student(1,"apple");
        Student student2 = new Student(1,"apple");
        System.out.println(student1.equals(student2)); // false
    }
}

因为继承Object类的equals方法所以还是比较地址。

  1. 重写equals方法
class Student {int id;
    String name;

    public Student(int id, String name) {this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {return Objects.hash(id, name);
    }
}
public class Test {public static void main(String[] args) {Student student1 = new Student(1,"apple");
        Student student2 = new Student(1,"apple");
        System.out.println(student1.equals(student2)); // true
        
    }
}

注意重写equals方法一般都要重写hashCode方法!!!

参考 – author:[多动手,勤思考]–为什么重写equals后需要重写hashCode

3.2.2 compareTo方法

泛型的比较接口类Comparable提供了compareTo抽象方法。

使用流程:实现Comparable接口重写compareTo方法

class Student implements Comparable{int id;
    String name;

    public Student(int id, String name) {this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name);
    }

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

    @Override
    public int compareTo(Student o) {return this.id - o.id;
    }
}
public class Test {public static void main(String[] args) {Student student1 = new Student(1,"apple");
        Student student2 = new Student(1,"apple");
        System.out.println(student1.compareTo(student2)); // 0
    }
}

注意事项:

implements Comparable– 括号内指明比较的对象。

public int compareTo(Student o) – 谁调用compareTo方法谁是this。


3.2.3 compare方法

泛型的比较接口类Comparator提供了compare抽象方法。

使用流程:子类继承Comparator重写compare方法。

sample 1:

class stuIdComparator implements Comparator{@Override
    public int compare(Student o1, Student o2) {return o1.id - o2.id;
    }
}	
public class Test {public static void main(String[] args) {Student student1 = new Student(1,"apple");
        Student student2 = new Student(1,"apple");
        System.out.println(comparator.compare(student1, student2)); // 0

    }
}

sample 2:

class stuIdComparator implements Comparator{@Override
    public int compare(Student o1, Student o2) {return o2.id - o1.id;// !!!
    }
}

class Student {int id;
    String name;

    public Student(int id, String name) {this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

public class Test {public static void main(String[] args) {Student student1 = new Student(1, "apple");
        Student student2 = new Student(2, "apple");
        Student[] students = new Student[]{student1,student2};
        stuIdComparator comparator = new stuIdComparator();
        Arrays.sort(students,comparator);// 传入比较器
        System.out.println(Arrays.toString(students));
    }
}

stdout:

[Student{id=2, name='apple'}, Student{id=1, name='apple'}]
3.2.4 compareTo() 和 compare()

何为侵入性? – 指代码产生对框架的依赖,离不开框架了。这里框架等价于这两个方法。

compareTo()对类的侵入性强。(翻译翻译)类一旦实现Comparable 接口必须重写该方法,而且如果改变比较的属性则要改变方法,那么调用改方法的对象可能也要做出相应的调整,所以说该方法对类的比较影响较大。

compare()对算法代码实现侵入性强。 该比较器可以根据需求去比较类中的属性,可以定义多个比较器,但是代码使用该方法则需实例化一个比较器。

一句话前者直接作用于类中,后者作用于类外。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


新闻标题:【JavaSE】Java中的数据比较-创新互联
文章路径:http://scyanting.com/article/dhsjgh.html