浅谈String类型等值比较引起的“==”、“equals()”和“hashCode”思考

关于String类型的等值比较和内容比较,是学习java甚至任何编程语言所共同的常见问题,理解String类型的等值比较和内容比较也是面试经常问到的问题。

String类型的等值比较和内容比较

字符串等值比较

public final class String
  implements java.io.Serializable, Comparable<String>, CharSequence {
  /** The value is used for character storage. */
  private final char value[];

   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的源码可以知道的是,java中等值比较,就是“==”比较,比较的是地址。字符串本质上是final修饰的字符数组,也就是说,当创建字符串对象时,字符串的引用是常量,但它每一个对象的值可以改变。

字符串内容比较

如上述String的源码可以知道的是,字符串的内容比较就是所谓的字符串的equals()方法,比较的是两个字符串对象储存的值,也就是内容是否相等,所谓内容相同,就是字符串每一个位置的字符相同。这里值得注意的是String重写了Object的equals()方法(Object的==与equals()是一样的)

地址分配图示

很明显str1和str2的地址相同,他们与str3的地址不相同,但是str3通过方法intern(),可以强制入池,和str1和str2的地址相同。

结论

字符串:默认为常量------------进常量池

String val = “xxx”;---------------------------默认入池

String val = new String(“xxx”);--------------------------默认入堆,但可以通过intern()强制入池(堆里的对象还在)

上面只是简单的介绍string类的对象等值比较,下面我们来深刻的了解下java 对象的等值判断。

对象相等判断

== 和 equals 的区别是什么

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

举个例子:

public class test1 {
  public static void main(String[] args) {
    String a = new String("ab"); // a 为一个引用
    String b = new String("ab"); // b为另一个引用,对象的内容一样
    String aa = "ab"; // 放在常量池中
    String bb = "ab"; // 从常量池中查找
    if (aa == bb) // true
      System.out.println("aa==bb");
    if (a == b) // false,非同一对象
      System.out.println("a==b");
    if (a.equals(b)) // true
      System.out.println("aEQb");
    if (42 == 42.0) { // true
      System.out.println("true");
    }
  }
}

说明:

String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。

当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。

hashCode 与 equals (重要)

HashSet如何检查重复

两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

hashCode和equals方法的关系

面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”

hashCode()介绍

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有 hashCode

我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

hashCode()与equals()的相关规定

如果两个对象相等,则hashcode一定也是相同的

两个对象相等,对两个对象分别调用equals方法都返回true

两个对象有相同的hashcode值,它们也不一定是相等的

因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

对象的相等与指向他们的引用相等,两者有什么不同?

对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。

以上这篇浅谈String类型等值比较引起的“==”、“equals()”和“hashCode”思考就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java中替代equals,compareTo和toString的方法

    我们都曾在POJO中重写过equals(),compareTo()和toString()方法.但是另有其他能做到职责分离的更好的方法并带来更简洁的代码.阅读这篇文章来一探究竟吧! 更简明的职责--摆脱equals.compareTo和toString方法 你曾经查看过java文档中的Object类吗?也许吧.每当你向上追溯继承树的时候都会止步于这个类.你会注意到,该类有几个方法是每一个类都必须继承的.而你最喜欢重写的方法可能就是toString(), .equals() and .hashCod

  • Java中Object.equals和String.equals的区别详解

    前言 Java中的堆和常量池的区别是什么呢?Object.equals与String.equals的区别呢?下面让我妈通过一个小示例让你明白它- 1.基础知识 Java的存储空间:寄存器.栈.堆.静态存储区.常量存储区(常量池).其他存储位置. 此处重点介绍堆和常量存储区: 堆:存储new的对象; 常量池:用来存储final static.String的常量. 2.Object.equals与String.equals的区别 Object.equals(==):比较内存地址: String.eq

  • Object类toString()和equals()方法使用解析

    我们知道,Object类是所有类的父类,因此也被称为根类.祖先.那么,我们就来看一看Object类的最常用的两个方法是如何用的. 1.toString方法: Object类的toString()方法默认返回的是对象地址值,如果某个类的toString()方法返回的不是对象地址值,则说明这个类重写了toString()方法. public class Test5 { public static void main(String[] args) { Person p1=new Person(); S

  • java 中String.equals和==的比较

     java 中String.equals和==的比较 初学java有段时间了,但是昨晚忽然就被"asd"==getpara("password")搞得不开心了:确实JAVA很多东西和以前接触过的语言完全不一样,比如最简单的new String("asd") !=new String ("asd"). 1 一个最简单的程序: public class A { public static void main(String args

  • 浅谈String类型等值比较引起的“==”、“equals()”和“hashCode”思考

    关于String类型的等值比较和内容比较,是学习java甚至任何编程语言所共同的常见问题,理解String类型的等值比较和内容比较也是面试经常问到的问题. String类型的等值比较和内容比较 字符串等值比较 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ pr

  • 浅谈String类型如何转换为time类型存进数据库

    目录 String转换为time存进数据库 这里就列举一个例子 java里面的类型是string类型 我使用的是java提供的传统的jdbc接口连接数据库 String类型的数字转换为时间日期格式 问题描述 String转换为time存进数据库 很久没试过将String类型转换并存进数据库中的date类型的字段,今天刚好遇到并解决了这个小问题,故写下来加深印象. 平时我们一般将数据库里面关于时间的处理字段设置为char之类的字符型,这样的好处是便于读取和存入,省掉了转换类型的麻烦. 但有时也会需

  • 浅谈String.valueOf()方法的使用

    前面的话 关于类型转换,对象常见的两个方法是toString()和valueOf().实际上,这两个方法也可以应用在包装类型上.前面已经介绍过toString()方法,本文将介绍valueOf()方法,该方法返回原值 [1]undefined和null没有valueOf()方法 undefined.valueOf();//错误 null.valueOf();//错误 [2]布尔型数据true和false返回原值 true.valueOf();//true typeof true.valueOf(

  • 浅谈char*类型返回值和字符串常量

    看这样一段简单的程序: #include <stdio.h> char* fun() { return "fun"; } int main() { printf("%s", fun()); return 0; } 这段程序可以正常run,但是最好不要这么做. 因为  直观上你返回了一个局部的东西出去.  你可以再外面定义这个常量,然后返回. 另外,字符串常量不可修改,而char*意味着要修改,故此最好加上const. 以上就是小编为大家带来的浅谈char

  • 浅谈C++类型转化(运算符重载函数)和基本运算符重载(自增自减)

    类型转化(运算符重载函数) 用转换构造函数可以将一个指定类型的数据转换为类的对象.但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据).在C++提供类型转换函数(type conversion function)来解决这个问题.类型转换函数的作用是将一个类的对象转换成另一类型的数据. 类型转换函数的一般形式为: operator 类型名( ){ 实现转换的语句 } 下面是简单实现.这时候,Base起了两方面的作用:类和数据类型.系统会在

  • 浅谈String.split()遇到空字符串的几种情况

    Java中的我们可以利用split把字符串按照指定的分割符进行分割,然后返回字符串数组 split 方法 该方法的作用是:将一个字符串分割为子字符串,然后将结果作为字符串数组返回. stringObj.split([separator,[limit]]) stringObj 必选项.要被分解的 String 对象或文字,该对象不会被split方法修改. separator 可选项.字符串或正则表达式对象,它标识了分隔字符串时使用的是一个还是多个字符.如果忽略该选项,返回包含整个字符串的单一元素数

  • 浅谈keras使用中val_acc和acc值不同步的思考

    在一个比较好的数据集中,比如在分辨不同文字的任务中,一下是几个样本 使用VGG19,vol_acc和acc基本是同步保持增长的,比如 40/40 [==============================] - 23s 579ms/step - loss: 1.3896 - acc: 0.95 - val_loss: 1.3876 - val_acc: 0.95 Epoch 13/15 40/40 [==============================] - 23s 579ms/st

  • 浅谈golang类型断言,失败类型断言返回值问题

    失败的类型断言,返回的值为最近断言类型的零值 代码入下: func main() { var data interface{} = "ehoo" if res, ok := data.(int); ok { fmt.Printf("int res:%d\n", res) } else if res, ok := data.(bool); ok { fmt.Printf("bool res:%b\n", res) } else { fmt.Prin

  • C#值类型、引用类型中的Equals和==的区别浅析

    引言 最近一个朋友正在找工作,他说在笔试题中遇到Equals和==有什么区别的题,当时跟他说如果是值类型的,它们没有区别,如果是引用类型的有区别,但string类型除外.为了证实自己的说法,也研究了一下,以免误导别人,这里将研究结果总结一下,如果我有什么地方说的不对的地方,望指出. 相等性 在定义类或结构时,您将决定为类型创建值相等性(或等效性)的自定义定义是否有意义. 通常,当类型的对象预期要添加到某类集合时,或者当这些对象主要用于存储一组字段或属性时,您将实现值相等性. 您可以基于类型中所有

  • 浅谈C++中的string 类型占几个字节

    在C语言中我们操作字符串肯定用到的是指针或者数组,这样相对来说对字符串的处理还是比较麻烦的,好在C++中提供了 string 类型的支持,让我们在处理字符串时方便了许多. 首先,我写了一段测试代码,如下所示: 复制代码 代码如下: #include <iostream>using namespace std; int main(void){ string str_test1; string str_test2 = "Hello World"; int value1, val

随机推荐