Java 中 == 与 equals 区别,再确认一遍?

本文详细解析了Java中equals方法与==操作符的区别,包括它们在基本数据类型与引用类型中的工作原理,以及String类型比较的特殊规则。

 

碰到“equals”和“==”这两个字符,老感觉差不多;其实还是有一些区别的,今天小编带大家一探究竟!

01、==介绍

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

举个例子

public class IntegerSame {    @Test    public void test() {        int i = 100;//基本数据类型        int ii = 100;//基本数据类型        Integer j = 100;//引用类型        Integer jj = 100;//引用类型        Integer k = new Integer(100);//引用类型        Integer kk = new Integer(100);//引用类型        System.out.println("i的地址:" + System.identityHashCode(i));        System.out.println("ii的地址:" + System.identityHashCode(ii));        System.out.println("j的地址:" + System.identityHashCode(j));        System.out.println("jj的地址:" + System.identityHashCode(jj));        System.out.println("k的地址:" + System.identityHashCode(k));        System.out.println("kk的地址:" + System.identityHashCode(kk));        //基本类型相互比较其中的值,所以得出true        System.out.println("i == ii 结果:" + (i == ii));        //当int的引用类型Integer与基本类型进行比较的时候,包装类会先进行自动拆箱        //然后与基本类型进行值比较,所有得出true        System.out.println("i == j 结果:" + (i == j));        //同上,包装类先拆箱成基本类型,然后比较,得出true        System.out.println("i == k 结果:" + (i == k));        //都是引用类型,所有比较的是地址,因为j与jj的地址相同,所有true        System.out.println("j == jj 结果:" + (j == jj));        //都是引用类型,所有比较的是地址,因为k与kk的地址相同,所有true        System.out.println("k == kk 结果:" + (k == kk));    }}class IntegerSame {

    @Test
    public void test() 
{
        int i = 100;//基本数据类型
        int ii = 100;//基本数据类型
        Integer j = 100;//引用类型
        Integer jj = 100;//引用类型
        Integer k = new Integer(100);//引用类型
        Integer kk = new Integer(100);//引用类型
        System.out.println("i的地址:" + System.identityHashCode(i));
        System.out.println("ii的地址:" + System.identityHashCode(ii));
        System.out.println("j的地址:" + System.identityHashCode(j));
        System.out.println("jj的地址:" + System.identityHashCode(jj));
        System.out.println("k的地址:" + System.identityHashCode(k));
        System.out.println("kk的地址:" + System.identityHashCode(kk));

        //基本类型相互比较其中的值,所以得出true
        System.out.println("i == ii 结果:" + (i == ii));
        //当int的引用类型Integer与基本类型进行比较的时候,包装类会先进行自动拆箱
        //然后与基本类型进行值比较,所有得出true
        System.out.println("i == j 结果:" + (i == j));
        //同上,包装类先拆箱成基本类型,然后比较,得出true
        System.out.println("i == k 结果:" + (i == k));

        //都是引用类型,所有比较的是地址,因为j与jj的地址相同,所有true
        System.out.println("j == jj 结果:" + (j == jj));
        //都是引用类型,所有比较的是地址,因为k与kk的地址相同,所有true
        System.out.println("k == kk 结果:" + (k == kk));
    }

}

输出结果:

640?wx_fmt=png

疑问点:为什么j和jj的地址是一样的,k与kk的地址却不一样呢?
答案:在-128~127的Integer值并且以Integer x = value;的方式赋值的参数,x会从包装类型自动拆箱成基本数据类型,以供重用!所以,j、jj的内存地址都是一样的!
下面我们把100变成1000试试!


public class IntegerSame {    @Test    public void test() {        int i = 10000;//基本数据类型        int ii = 10000;//基本数据类型        Integer j = 10000;//引用类型        Integer jj = 10000;//引用类型        Integer k = new Integer(10000);//引用类型        Integer kk = new Integer(10000);//引用类型        System.out.println("i的地址:" + System.identityHashCode(i));        System.out.println("ii的地址:" + System.identityHashCode(ii));        System.out.println("j的地址:" + System.identityHashCode(j));        System.out.println("jj的地址:" + System.identityHashCode(jj));        System.out.println("k的地址:" + System.identityHashCode(k));        System.out.println("kk的地址:" + System.identityHashCode(kk));        //基本类型相互比较其中的值,所以得出true        System.out.println("i == ii 结果:" + (i == ii));        //当int的引用类型Integer与基本类型进行比较的时候,包装类会先进行自动拆箱        //然后与基本类型进行值比较,所有得出true        System.out.println("i == j 结果:" + (i == j));        //同上,包装类先拆箱成基本类型,然后比较,得出true        System.out.println("i == k 结果:" + (i == k));        //都是引用类型,所有比较的是地址,因为j与jj的地址相同,所有true        System.out.println("j == jj 结果:" + (j == jj));        //都是引用类型,所有比较的是地址,因为k与kk的地址相同,所有true        System.out.println("k == kk 结果:" + (k == kk));    }}class IntegerSame {

    @Test
    public void test() 
{
        int i = 10000;//基本数据类型
        int ii = 10000;//基本数据类型
        Integer j = 10000;//引用类型
        Integer jj = 10000;//引用类型
        Integer k = new Integer(10000);//引用类型
        Integer kk = new Integer(10000);//引用类型
        System.out.println("i的地址:" + System.identityHashCode(i));
        System.out.println("ii的地址:" + System.identityHashCode(ii));
        System.out.println("j的地址:" + System.identityHashCode(j));
        System.out.println("jj的地址:" + System.identityHashCode(jj));
        System.out.println("k的地址:" + System.identityHashCode(k));
        System.out.println("kk的地址:" + System.identityHashCode(kk));

        //基本类型相互比较其中的值,所以得出true
        System.out.println("i == ii 结果:" + (i == ii));
        //当int的引用类型Integer与基本类型进行比较的时候,包装类会先进行自动拆箱
        //然后与基本类型进行值比较,所有得出true
        System.out.println("i == j 结果:" + (i == j));
        //同上,包装类先拆箱成基本类型,然后比较,得出true
        System.out.println("i == k 结果:" + (i == k));

        //都是引用类型,所有比较的是地址,因为j与jj的地址相同,所有true
        System.out.println("j == jj 结果:" + (j == jj));
        //都是引用类型,所有比较的是地址,因为k与kk的地址相同,所有true
        System.out.println("k == kk 结果:" + (k == kk));
    }

}

输出结果:

640?wx_fmt=jpeg

当j、jj超出-128~127区间的时候,地址就变了,所以比较的结果就是false。
再看其它的包装器自动拆箱情况:


类型描述
Boolean全部自动拆箱
Byte全部自动拆箱
Short-128~127区间自动拆箱
Integer-128~127区间自动拆箱
Long-128~127区间自动拆箱
Float没有拆箱
Doulbe没有拆箱
Character0~127区间自动拆箱

02、equals()方法介绍

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

Boolean、Byte、Short、Integer、Long、Float、Doulbe、Character 8种基本类型的包装类都重写了 equals() 方法,所以比较的时候,如果内容相同,则返回 true,例如:

//因为内容相同,返回的都是trueSystem.out.println("j.equals(jj) 结果:" + (j.equals(jj)));System.out.println("(k.equals(kk) 结果:" + (k.equals(kk)));
System.out.println("j.equals(jj) 结果:" + (j.equals(jj)));
System.out.println("(k.equals(kk) 结果:" + (k.equals(kk)));

03、String类型的比较介绍

string是一个非常特殊的数据类型,它可以通过String x = value;的方式进行赋值,也可以通过String x = new String(value)方式进行赋值。

String x = value;方式赋予的参数,会放入常量池内存块区域中;
String x = new String(value)方式赋予的参数,会放入堆内存区域中,当成对象处理。


举个例子:

public class DemoEquals {    public static void main(String[] args) {        String a = new String("ab"); // a 为一个引用        String b = new String("ab"); // b为另一个引用,对象的内容一样        String aa = "ab"; // 放在常量池中        String bb = "ab"; // 从常量池中查找        System.out.println("a地址:" + System.identityHashCode(a));        System.out.println("b地址:" + System.identityHashCode(b));        System.out.println("aa地址:" + System.identityHashCode(aa));        System.out.println("bb地址:" + System.identityHashCode(bb));        //地址相同,所以返回true        if (aa == bb) {            System.out.println("aa==bb");        }        // 地址不同,非同一个对象,所以返回false        if (a == b) {            System.out.println("a==b");        }        //地址不同,但是内容相同,所以返回true        if (a.equals(b)) {            System.out.println("aEQb");        }    }}class DemoEquals {

    public static void main(String[] args{
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b为另一个引用,对象的内容一样
        String aa = "ab"// 放在常量池中
        String bb = "ab"// 从常量池中查找
        System.out.println("a地址:" + System.identityHashCode(a));
        System.out.println("b地址:" + System.identityHashCode(b));
        System.out.println("aa地址:" + System.identityHashCode(aa));
        System.out.println("bb地址:" + System.identityHashCode(bb));
        //地址相同,所以返回true
        if (aa == bb) {
            System.out.println("aa==bb");
        }
        // 地址不同,非同一个对象,所以返回false
        if (a == b) {
            System.out.println("a==b");
        }
        //地址不同,但是内容相同,所以返回true
        if (a.equals(b)) {
            System.out.println("aEQb");
        }
    }
}

输出结果:

640?wx_fmt=png

为什么string的equals()方法比较返回true,因为string重写了equals()方法,源码如下:


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;}
    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;
}

如果内容相同,则返回true!

总结:如果需要比较某个对象是否相同,一定要重写equals(),比较其中的内容是否相同,如果相同,返回true;否则,返回false!


作者简介:

炸鸡可乐,一个在路上的程序员,坚信路不在遥远,贵在坚持✊



精彩回顾:

并发程序设计,懂不懂?

TDD(测试驱动开发)死了吗?

Spring 源码学习(二)-默认标签解析


强烈推荐:

《Java 极客技术》知识星球限时优惠,现在加入只需 50 元,仅限前 1000 名,机不可失时不再来。长按识别下面的二维码即可加入,趁早行动吧!


640?wx_fmt=png


隆重介绍:

Java 极客技术公众号,是由一群热爱 Java 开发的技术人组建成立,专注分享原创、高质量的 Java 文章。如果您觉得我们的文章还不错,请帮忙赞赏、在看、转发支持,鼓励我们分享出更好的文章。


640?wx_fmt=jpeg


<think>好的,我现在需要详细分析ArrayList在JDK 8中的indexOf方法的源码实现。首先,我得回忆一下ArrayList的基本结构。ArrayList是基于数组实现的,所以它的元素是存储在数组中的。indexOf方法的作用是返回指定元素第一次出现的索引,如果不存在则返回-1。那么,它的实现应该涉及遍历数组,逐个比较元素。 接下来,我需要找到JDK 8中ArrayList的源码。根据之前学过的知识,ArrayList的源码可以在java.util包下找到。打开ArrayList类,搜索indexOf方法。找到方法后,先看方法的签名:public int indexOf(Object o)。参数是Object类型,说明可以接受任何对象,包括null。 然后看方法体。首先会检查传入的元素是否为null。如果是null,就遍历数组,找到第一个为null的元素的位置。这里使用for循环,从索引0开始,直到size-1,逐个检查elementData[i]是否等于null。找到后立即返回索引i。 如果元素不为null,则同样遍历数组,但这次使用equals方法来判断元素是否相等。这里要注意,equals方法是非静态的,所以需要确保数组中的元素不为null才能调用,否则会抛出NullPointerException。因此,当o不为null时,遍历时会用o.equals(elementData[i])来比较每个元素是否相等,找到第一个相等的元素就返回其索引。 这里有个关键点,为什么在o不为null的情况下不使用elementData[i].equals(o)呢?因为如果elementData[i]为null的话,调用equals会抛出异常。而反过来,o已经确定不为null,所以调用o.equals(elementData[i])是安全的,即使elementData[i]为null,也只会返回false而不会导致异常。 另外,需要注意elementData数组的实际长度可能大于size,所以遍历时只检查到size-1为止,而不是到elementData.length-1。这是因为ArrayList的size表示列表中元素的数量,而elementData数组可能有空闲的位置,这些位置未被使用,不需要检查。 还有一个细节是,indexOf方法的时间复杂度是O(n),因为最坏情况下需要遍历整个数组。这在ArrayList的文档中也有说明,所以使用时要注意性能问题。 现在,结合引用的资料,比如引用[3]中提到ArrayList的get方法通过数组下标直接访问,时间复杂度是O(1),而indexOf需要遍历,所以是O(n)。这也符合预期。 最后,确认源码中的具体实现是否有其他特殊处理,比如并行处理或者优化。根据JDK 8的源码,indexOf方法并没有使用任何并行处理,只是简单的顺序遍历。因此,实现相对直接,但效率取决于元素的位置,越早找到越快结束。</think>### ArrayList JDK 8 源码 `indexOf` 方法详解 #### 1. 方法作用 `indexOf(Object o)` 用于返回元素 `o` 在 `ArrayList` 中**第一次出现的位置索引**,若未找到则返回 `-1`。此方法区分 `null` 值,并依赖 `equals()` 方法进行对象比较。 #### 2. 源码实现 JDK 8 中 `ArrayList` 的 `indexOf` 方法源码如下: ```java 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; } ``` #### 3. 实现原理分步解析 1. **参数检查** - 首先判断 `o` 是否为 `null`。若为 `null`,则遍历数组寻找第一个 `null` 元素。 - 若 `o` 非 `null`,则遍历数组并调用 `o.equals(elementData[i])` 比较元素是否相等。 2. **遍历逻辑** - 遍历范围:从索引 `0` 到 `size-1`(`size` 是实际元素数量),而非数组总长度 `elementData.length`。这是为了避免访问未使用的数组空间[^3]。 - 时间复杂度:最坏情况下为 $O(n)$(遍历整个数组)。 3. **比较方式** - **`null` 处理**:直接通过 `==` 判断数组元素是否为 `null`。 - **非 `null` 处理**:调用 `o.equals(elementData[i])` 比较,而非 `elementData[i].equals(o)`。此设计避免因 `elementData[i]` 为 `null` 而抛出 `NullPointerException`[^3]。 #### 4. 关键设计点 - **空值安全性**:通过优先判断 `o` 是否为 `null`,确保方法在遇到 `null` 元素时不会崩溃。 - **效率优化**:找到目标后立即返回索引,减少不必要的遍历。 - **依赖 `equals()`**:对象的比较基于 `equals()` 方法,因此需确保元素正确覆写此方法以实现预期逻辑[^2]。 #### 5. 示例场景 ```java ArrayList<String> list = new ArrayList<>(); list.add("A"); list.add(null); list.add("B"); System.out.println(list.indexOf(null)); // 输出 1 System.out.println(list.indexOf("B")); // 输出 2 System.out.println(list.indexOf("C")); // 输出 -1 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值