Java集合

String源码阅读

2018-08-10  本文已影响62人  uranusleon

String源码阅读

wiki

学习目标

内存角度理解String

String str = new String("uranus"); // 堆中有String实例对象,常量池中有String实例

String str1 = "uranus"; //常量池中有string实例

String str2 = new String("uranus").intern(); //堆中有实例对象,常量池中有String实例对象

String str3 = "uranus" + "leon";//常量池中有uranusleon字符串的实例,没有uranus和leon的实例

String str1 = "uranus";
String str4 = str1 + "leon";//堆中有uranusleon的实例,常量池中没有uranusleon的实例,没有leon的实例

String str5 = new String("uranus") + new String("leon"); // 堆中有uranus和leon的实例,在常量池中是否有uranusleon这个字符串

new String("uranusleon")

package StringTest;

public class StringBasic {
    public static void main(String[] args)
    {
        String s1 = "uranusleon";
        String s2 = new String("uranusleon");
        String s3 = new String("uranusleon").intern();

        System.out.println(s1 == s2); // false
        System.out.println(s1 == s3); // true
    }
}

new String()生成了几个变量

若常量池中已经存在字面量,则直接引用,也就是此时只会创建一个对象,如果常量池中不存在字面量,则先创建后引用,也就是有两个

String字面量进入字符串常量池的时机

String.intern()的理解

intern()的作用

当一个String实例调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");// 堆中创建对象“11”,String常量池中没有“11”
    s3.intern(); //JDK6中在String常量池中新创建一个对象,JDK7中在String常量池中保存堆中对象的引用
    String s4 = "11"; //JDK6中S4是常量池中对象的引用,JDK7中s4是堆中对象的引用
    System.out.println(s3 == s4); //JDK6中s3!=s4,JDK7中s3 == s4
}

打印结果

public static void main(String[] args) {
    String s = new String("1");
    String s2 = "1";
    s.intern();
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1"); // 堆中创建对象“11”,String常量池中没有“11”
    String s4 = "11"; //jdk6和jdk7都会在常量池中创建“11”的对象
    s3.intern(); // 发现常量池中有“11”,不需要新建或者保存堆中对象的引用
    System.out.println(s3 == s4);
}

打印结果

原因

intern()的使用时机

对于可能经常使用的字符串,并且这些字符串在编译期无法确定,只能在运行期才可以确定,可以使用intern()将字符串加入字符串常量池。

static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];

public static void main(String[] args) throws Exception {
    Integer[] DB_DATA = new Integer[10];
    Random random = new Random(10 * 10000);
    for (int i = 0; i < DB_DATA.length; i++) {
        DB_DATA[i] = random.nextInt();
    }
    long t = System.currentTimeMillis();
    for (int i = 0; i < MAX; i++) {
        //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
         arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
    }

    System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}

intern()理解的要点

由上述讲解可以看出,在判断字符串的引用在字符串常量池中是否存在主要看class文件的常量池中是否存在字符串的字面量。

String + 的实现

String源码

构造方法

比较方法

public boolean equals(Object anObject);
public boolean contentEquals(CharSequence cs);
public boolean contentEquals(StringBuffer sb);
public boolean equalsIgnoreCase(String anotherString);
public int compareTo(String anotherString);
public int compareToIgnoreCase(String str);
public boolean regionMatches(boolean ignoreCase, int toffset,
            String other, int ooffset, int len); //Tests if two string regions are equal.
public boolean regionMatches(int toffset, String other, int ooffset,int len);

equal()方法

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) { // value是私有的,怎么可以直接访问?
                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;
    }

contentEquals()方法

public boolean contentEquals(CharSequence cs) {
        // Argument is a StringBuffer, StringBuilder
        if (cs instanceof AbstractStringBuilder) {
            if (cs instanceof StringBuffer) {
                synchronized(cs) {
                   return nonSyncContentEquals((AbstractStringBuilder)cs);
                }
            } else {
                return nonSyncContentEquals((AbstractStringBuilder)cs);
            }
        }
        // Argument is a String
        if (cs instanceof String) {
            return equals(cs);
        }
        // Argument is a generic CharSequence
        char v1[] = value;
        int n = v1.length;
        if (n != cs.length()) {
            return false;
        }
        for (int i = 0; i < n; i++) {
            if (v1[i] != cs.charAt(i)) {
                return false;
            }
        }
        return true;
    }

equalsIgnoreCase(String anotherString)

public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }

compareTo()和compareToIgnoreCase()方法

Hashcode()方法

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

subString()方法

调用public String(char value[], int offset, int count);方法生成一个新的String实例

replace方法

1)replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换 2)replaceAll和replaceFirst的参数是regex,即基于规则表达式的替换,比如,可以通过replaceAll(“\d”, “*”)把一个字符串所有的数字字符都换成星号; 相同点是都是全部替换,即把源字符串中的某一字符或字符串全部换成指定的字符或字符串, 如果只想替换第一次出现的,可以使用 replaceFirst(),这个方法也是基于规则表达式的替换,但与replaceAll()不同的是,只替换第一次出现的字符串; 另外,如果replaceAll()和replaceFirst()所用的参数据不是基于规则表达式的,则与replace()替换字符串的效果是一样的,即这两者也支持字符串的操作;

codePointAt方法

public int codePointAt(int index);
public int codePointBefore(int index);
public int codePointCount(int beginIndex, int endIndex);
public int offsetByCodePoints(int index, int codePointOffset);

public int codePointAt(int index);的作用是返回索引出字符的Code Point,如果此索引出的字符是Surrogate High,下一个索引的字符是Surrogate Low,则返回Surrogate Pair对应的Code Point。

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = new String(Character.toChars(uni));
    System.out.println(str.codePointAt(0)); //输出128657
}

public int codePointCount(int beginIndex, int endIndex);计算字符串的Char[]从beginIndexendIndex-1之间Code Point的数目(Surrogate Pair算为一个),Unpaired surrogates算为一个;

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = new String(Character.toChars(uni));
    System.out.println(str.codePointCount(0,1)); //输出1
    System.out.println(str.length()); //输出2
}

public int codePointBefore(int index),如果字符数组中index-2的值是Surrogate High,index-1的值是Surrogate Low,则返回index-2和index-1组成的Surrogate Pair的Code Point,否则只返回index-1对应的code point。

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = new String(Character.toChars(uni)) + "unicode";
    System.out.println(str.codePointBefore(2)); //输出 128657
}

public int offsetByCodePoints(int index, int codePointOffset)方法返回String中从给定的index偏移codePointOfferSet个code points的索引

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = "uni" + new String(Character.toChars(uni)) + "code";
    System.out.println(str.offsetByCodePoints(0,4)); //输出 5
}

concat()方法

public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

indexOf()

public int indexOf(int ch);
public int indexOf(int ch, int fromIndex);
private int indexOfSupplementary(int ch, int fromIndex);//indexOf(int ch, int fromIndex)调用
public int indexOf(String str);
public int indexOf(String str, int fromIndex);
static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex);

matches()方法

String.matches()方法匹配整个字符串是否符合正则表达式,不是匹配部分字符串

上一篇 下一篇

猜你喜欢

热点阅读