String类详解及引伸来的equals,==,hashcode区别和StringBuilder和StringBuffer比较

1.StringBuilder和StringBuffer比较

首先拓展了解一下String类,

它是不可改变的对象

如以下语句:

	String s = "ABC";
	System.out.println("s:" + s);
	
	s = "123";
	System.out.println("s:" + s);

很容易的知道输出会是:

ABC
123

但是,s只是一个对象的引用,ABC并未被改变
String类详解及引伸来的equals,==,hashcode区别和StringBuilder和StringBuffer比较_第1张图片
再进入String类的源码中查看,可发现,String其实是字符数组,而且用private final修饰,初始化后不可改变
String类详解及引伸来的equals,==,hashcode区别和StringBuilder和StringBuffer比较_第2张图片
可见,对String类的操作基本都是重新创建一个String对象,再返回该对象来完成的,并非在原对象上操作
但是,并非无法更改原来的值,可以通过修改指向的value[]数组中的值来更改,这需要利用到反射,具体可参考(https://blog.csdn.net/zhangjg_blog/article/details/18319521)


String的创建方式有两种:String a = “abc”; String b=new String(“abc”);

两者有什么区别?

第一种会先在字符串常量池中寻找是否已经存在"abc"常量,若没有则创建该常量,并将此常量的引用返回给String a;如果已有"abc" 常量,则直接返回String constant pool 中“abc” 的引用给String a.此创建方法会在字符串常量池中创建对象。
第二种,会直接在字符串常量池 中判断是否有,没有就创建。同时,遇到new关键字,也会在堆内存中再创建Stirng对象,并把"abc"的value和hash赋值给他,最后把该对象的引用返回给String b;
通过new String()创建的对象可通过调用intern()方法将字符串添加到常量池中。
具体如下:
String类详解及引伸来的equals,==,hashcode区别和StringBuilder和StringBuffer比较_第3张图片
现在便理解以下代码的原因了

		String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1 == s2);

输出为:true
这里,可能会想到用s1.equals(s2)会怎么样嘞,更改后,发现输出为true,为什么?

引出下一个疑问,== 和 equals的区别

首先来看

== 比较的是什么

		String n = "hello";
        String m = "hello";

        System.out.println(n==m);

        String str = new String("hello");
        String str1 = new String("hello");
        String str2 = new String("hello");

        System.out.println(str1==str2);

        str1 = str;
        str2 = str;
        
        System.out.println(str1==str2);

输出为:

true
false
true

可见,上面说到String str = ""是创建了一个字符串常量,因而n和m比较的是两个的值,明显是相等的。
下面的str1和str2是新建的String类对象,这里的比较值,应该想到的是比较两个对象的地址值,可知,两个对象的地址肯定是不相同的,因此输出为false。
最后,将str1和str2同时指向str的地址,所以,输出为true
最后,应该明白了==比较的是值,如基本数据类型的值以及引用类型的地址值
下面来看

equals比较的是什么

equals的Object提供的方法,源码如下:

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

可见,Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。
但是下面代码:

		String str1 = new String("hello");
        String str2 = new String("hello");
        
        System.out.println(str1.equals(str2));

输出的为什么会是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;
    }

很明显,是对String的每个char进行的对比,用来比较指向的字符串对象所存储的字符串是否相等。
总结:
==,作用于基本数据类型,则直接比较其存储的 “值”是否相等;如果作用于引用类型,则比较的是所指向的对象的地址
equals,如果该类型未对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;但是如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

偶然间看到有关equals()和hashcode()之间的关联,看来也有必要了解下

hashCode() :Object中的方法,用于获取哈希码,也称为散列码;返回一个int整数。作用是确定该对象在哈希表中的索引位置。比如HashMap,HashSet等散列表中。以下参考(https://www.cnblogs.com/skywang12345/p/3324958.html)

  • 当比较非散列表对象时,也就是不会在散列表用存储该对象时,显而易见,equals()和hasncode()并无对应关系
  • 但是,用于散列表中的时候,参考以下例子
import java.util.HashSet;
import java.util.Iterator;

public class equals_hashcode {
    public static class Person{
        int age;
        String name;

        Person(String name, int age){
            this.age = age;
            this.name = name;
        }

        public String Print(){
            return name+ "--"+ age;
        }

        @Override
        public int hashCode() {
            int code = name.toUpperCase().hashCode();
            return  code * age;//只是用于充当哈希码
        }

        @Override
        public boolean equals(Object obj) {
            if(obj == null){
                return false;
            }
            if(this == obj){
                return true;
            }

            if(this.getClass() != obj.getClass()){
                return false;
            }
            Person person = (Person)obj;
            return name.equals(person.name) && age == person.age;
        }
    }

    public static void main(String[] args){
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);

        HashSet hashSet = new HashSet<Person>();
        hashSet.add(p1);
        hashSet.add(p2);
        hashSet.add(p3);
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n",p1.equals(p2), p1.hashCode(), p2.hashCode());
        System.out.printf("p1.equals(p3) : %s; p1(%d) p3(%d)\n",p1.equals(p3), p1.hashCode(), p3.hashCode());
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()){
            Person person =(Person) iterator.next();
            System.out.println(person.Print());
        }
    }
}

Person重写了父类的equals和hashcode方法,HashSet不能有重复的元素。
当未重写父类hashcode()方法时,对应输出如下:

p1.equals(p2) : true; p1(460141958) p2(1163157884)
p1.equals(p3) : false; p1(460141958) p3(1956725890)
aaa–200
eee–100
eee–100
可以看到,即使p1和p2相等,但是由于哈希码的不同,仍然可以存储在hashSet中。

下面为重写hashcode后的输出:

p1.equals(p2) : true; p1(6851700) p2(6851700)
p1.equals(p3) : false; p1(6851700) p3(12909000)
eee–100
aaa–200
因为p1,p2包括哈希码都相等了,算是重复元素,所以hashSet无该值

现在回到正题

StringBuilder和StringBuffer

都是继承自AbstractStringBuilder,且都是Char[] value,char类型的数组

extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

比较两者的源码,可看出StringBuffer在每个方法中加了关键词synchronized(同步锁),用于防止多个线程同时调用该方法,所以是线程安全的,StringBuilder则是线程不安全的

  • 三者在执行速度方面的比较:StringBuilder > StringBuffer > String
    原因:
    • String:字符串常量
    • StringBuffer:字符串变量,但是加了同步锁
    • StringBuilder:字符串变量

你可能感兴趣的:(学习,java,StringBuilder,hashcode,String,equasl)