从头开始学JDK-------String

目录

* String #倒序

* String #构造函数

* String #equals

* String #内存

* String #hashcode

* String #startsWith

* String #endWith

* String #indexOf

* String #lastIndexOf

* String #subString(int beginIndex)

* String static#join(String separator , String... elements)

* String #split(String regex)

* String #replace(char,char)

* String #replaceFirst(String regex , String replacement)

* String #matches(String regex) 

* String #contains


* String #倒序

    public static boolean reverseEquals(String str) {
        return Objects.equals(str, new StringBuilder(str).reverse().toString());
    }

        下面的代码是不允许使用任何其它类的实现,前后分别创建了一个指针。

 

    public static boolean reverseEquals2(String str) {
        Objects.requireNonNull(str);
        int startIndex = 0;
        int endIndex = str.length() - 1;

        int middle = str.length() / 2;

        for (int i = 0; i < middle; i++) {
            char start = str.charAt(startIndex);
            char end = str.charAt(endIndex);
            if (!Objects.equals(start, end)) {
                return false;
            }
            startIndex++;
            endIndex--;
        }

        return true;
    }

* String #构造函数

        String字符串内部维护了一个char[]数组。不管是哪个构造函数,最终都是解析成char[]数组。

   public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    public String() {
        this.value = new char[0];
    }

    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

*  衍生Arrays.copyOf(数组[],新长度)

         Arrays.copyOf(基本类型[],新长度) ,这个方法重载了很多种,就是把一个基本类型的数组的值,复制一个新的长度的数组,如果长度大于以前的数组,则用默认值填充。

        如果是引用数据类型的话,那么就是重新创建一个容器,但是里面的元素是浅复制,指向的与源数组是同一个对象。   

        class Temp{
        }

        Temp[] a = {new Temp(),new Temp()};

        Temp[] b = Arrays.copyOf(a,a.length);
        System.out.println(Arrays.equals(a,b));

       

*  衍生Arrays.equals(a,b)

         比较的是每个数组对应的角标上的元素,调用它们的equals()方法。     

class Arrays  

public static boolean equals(Object[] a, Object[] a2) {

        //....

        for (int i=0; i<length; i++) {
            Object o1 = a[i];
            Object o2 = a2[i];
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }

        return true;
    }

* String #equals

         Object类中定义了一个equals方法。

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

        String类重写了equals方法,与另外一个字符串类中char[]数组(引用名为value),比较的是它们的value数组是否每个成员都一样。

        PS:写到这里的时候,我突然想,为什么JDK大佬不直接用Arrays#equals方法直接比较,原来Arrays是JDK1.2开始使用的。。水一下

class String

    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 #内存

        如果直接使用 == ,比较的是字符串对象的内存地址。

        String a = new String("abc");创建了两个字符串对象,一个是在字符串常量池里,另外一个在堆内存里。

        构造函数中有一个 new String(源字符串),把源字符串的 char[] value(基本数据类型)直接赋值给这个新构造的字符串对象。

        它们两个使用" == "比较的话,返回false,因为内存不同。

        如果使用 #quals(Object)方法比较的话,返回true。因为内部的char[]数组是一样的。

        String a = "abc";
        String b = new String(a);

        System.out.println(a == b);//内存地址不一样false
        System.out.println(a.equals(b));//char[]数组一样true

        System.out.println(a == b.intern());//b.intern返回了字符串常量池的a,true

 

* String #hashcode

        对char[]数组的每个元素进行运算,近似理解成对每个字符的ASCII码 与 31做运算,并求和。

        a的ASCII码是97,b的是98。

        为什么使用31作为乘子,是因为31作为一个既不太大又不太小的乘子,计算出来的hashcode值范围处于一个“适中”的区间,能够很好的降低哈希冲突

 

* 衍生 为什么我们在使用HashMap的时候总是用String做key?

        因为字符串是不可变的,当创建字符串时,它的它的hashcode被缓存下来,不需要再次计算。因为HashMap内部实现是通过key的hashcode来确定value的存储位置,所以相比于其他对象更快。这也是为什么我们平时都使用String作为HashMap对象。

* String #startsWith

        本质上比较的是char[]数组的前几个字符是否相同。源码中果然也是创建了两个指针。

* String #endWith

        endWith从另外一个角度看就是startsWith。指针从 【value.length - suffix.value.length】开始。

* String #indexOf

        如果是一个字符串,找到这个字符串第一次出现的地方。匹配的还是char[] value。

        String a = "abcabcadef";

        //找到ca第一次出现的位置,并且截取它后面的字符串
        String s = "ca";
        System.out.println(a.indexOf(s));
        System.out.println(a.substring(a.indexOf(s) + s.length()));

        int fromIndex = 3;
        System.out.println(a.substring( a.indexOf(s,fromIndex) + s.length()));//def

* String #lastIndexOf

         从右向左遍历char[]

* String #subString(int beginIndex)

         创建一个新的对象,源码上首先通过beginIndex确定了要截取的子字符串长度是多少,然后使用Arrays.copyOfRange方法复制char[]

    public String substring(int beginIndex) {
        //remove some code
        int subLen = value.length - beginIndex;
        return (beginIndex == 0) ? 
               this : new String(value,beginIndex,subLen);
    }

    String(char[],int beginIndex,int count){
       //remove some code
       this.value = Arrays.copyOfRange(value, beginIndex, subLen)
    }

* String static#join(String separator , String... elements)

        第一个是分隔符,第二个是可变参数。源代码中使用的是JDK1.8出现得StringJoiner类,该类是面向对象编码的典范。

    public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
  
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

public final class StringJoiner ...

    private final String prefix;
    private final String delimiter;
    private final String suffix;
    private String emptyValue;

    public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        Objects.requireNonNull(prefix, "The prefix must not be null");
        Objects.requireNonNull(delimiter, "The delimiter must not be null");
        Objects.requireNonNull(suffix, "The suffix must not be null");

        // make defensive copies of arguments
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix;
    }

* String #split(String regex)

        按照指定正则分割,返回String[]

        String a = "a,b,c,d,e";
        //[a, b, c, d, e]
        System.out.println(Arrays.toString(a.split(",")));

* String #replace(char,char)

        把源字符串的char[]中的所有字符替换成另外一个,并且返回一个新的字符串对象。

    public String replace(char oldChar, char newChar) {
        
        //buf is a new char[] after replace all oldChar using newChar
            
        return new String(buf, true);
    }

* String #replaceFirst(String regex , String replacement)

        把符合正则的第一处,替换成replacement。当然还有replaceAll(String regex,String replacement)

* String #matches(String regex) 

        是否符合指定正则

* String #contains

        是否包含,源码思路:indexOf指定字符串,看是否返回 大于 -1 。 -1代表没找到

       

posted @ 2022-07-17 12:13  小大宇  阅读(1)  评论(0编辑  收藏  举报