zoukankan      html  css  js  c++  java
  • String详解

    String详解

    Posted on2016-11-20 21:27旧巷的常青藤阅读(6) 评论(0)编辑收藏

    在开发中,我们都会频繁的使用String类,掌握String的实现和常用方法是必不可少的,当然,我们还需要了解它的内部实现。

    一. String的实现

    在Java中,采用了一个char数组实现String类型,这个char数组被定义为final类型,这就意味着一旦一个String被创建,那么它就是不可变的。除此之外,还定义了一个int类型的hash,用来保存该String的hash值。

     /** The value is used for character storage. */
        private final char value[];
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0

    二. String中的构造器

    创建String的方法很多,构造器也有多个。但是其目的就是给value数组赋值。构造器中传入的参数大致可以分为几个部分:

    src:来源,就是希望在value中保存的字符串,传入的值可以是String,char数组,int数组,byte数组,Stringbuffer,StringBuilder,boolean值。

    offset:src中字符串的起始位置。

    count:src中赋值到value中的字符串的个数。

    Charset:指定字符集。

    当然,不是每一个构造器都需要这些参数,我们也不需要一个个都详细掌握,只要知道大概存在哪些构造方法即可,要用的时候可以查询API。

    三. String中常用的方法以及实现

    1. 获取字符数组

    获取字符数组方法:

    1
    publicvoidgetChars(intsrcBegin,intsrcEnd,chardst[],intdstBegin)

    该方法将String中指定位置的字符复制个dst,具体的实现如下:

    复制代码
     public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        //对指定位置进行判断
            if (srcBegin < 0) {
                throw new StringIndexOutOfBoundsException(srcBegin);
            }
            if (srcEnd > value.length) {
                throw new StringIndexOutOfBoundsException(srcEnd);
            }
            if (srcBegin > srcEnd) {
                throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
            }
        //调用native方法进行数组复制
            System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
        }
    复制代码

    2. equals()、equalsIgnoreCase()、regionMatches()、compareTo()、compareToIgnoreCase()、hashCode()

    equals()方法用来判断两个String是否相等,实现逻辑如下:

    复制代码
     public boolean equals(Object anObject) {
            if (this == anObject) {//如果两个String是引用同一个String对象,则相等
                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;
        }
    复制代码

    equalsIgnoreCase()忽略大小写进行判断,其内部实现是调用regionMatches()方法。

      regionMatches()方法是用来判断两个字符串区域是否相等。String中有两个regionMatches()方法,不同之处在于有一个增加一个boolean值决定是否忽略大小写进行判断。具体的实现如下:

    复制代码
     public boolean regionMatches(boolean ignoreCase, int toffset,
                String other, int ooffset, int len) {
            char ta[] = value;
            int to = toffset;
            char pa[] = other.value;
            int po = ooffset;
            if ((ooffset < 0) || (toffset < 0)
                    || (toffset > (long)value.length - len)
                    || (ooffset > (long)other.value.length - len)) {
                return false;//不满足以上条件的都返回false
            }
            while (len-- > 0) {
                char c1 = ta[to++];
                char c2 = pa[po++];
                if (c1 == c2) {
                    continue;
                }
                if (ignoreCase) {//是否区分大小写。
                    char u1 = Character.toUpperCase(c1);
                    char u2 = Character.toUpperCase(c2);
                    if (u1 == u2) {
                        continue;
                    }   
                    if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
                        continue;
                    }
                }
                return false;
            }
            return true;
        }
    复制代码

      另外还存在一个判断两个字符串区是否相等的方法,但是是区分大小写的:public boolean regionMatches(int toffset, String other, int ooffset, int len),但是这个方法内部实现没有复用上面这个方法。

      compareTo()方法是用来比较两个字符串大小,比较规则为:按字典顺序比较两个字符串。该比较基于字符串中各个字符的 Unicode 值。将此String对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此String对象在参数字符串之前,则比较结果为一个负整数。如果按字典顺序此String对象位于参数字符串之后,则比较结果为一个正整数。如果这两个字符串相等,则结果为 0。具体实现如下:  

    复制代码
     public int compareTo(String anotherString) {
            int len1 = value.length;
            int len2 = anotherString.value.length;
            int lim = Math.min(len1, len2);
            char v1[] = value;
            char v2[] = anotherString.value;
    
            int k = 0;
            while (k < lim) {
                char c1 = v1[k];
                char c2 = v2[k];
                if (c1 != c2) {
                    return c1 - c2;//字符不等,返回Unicode差值。
                }
                k++;
            }
            return len1 - len2;//两个字符串长度,返回长度差值。若为0,表示两字符串大小相等。
        }
    复制代码

      compareToIgnoreCase()方法实现:String在内部自定义了一个名为CaseInsensitiveComparator的类,实现Comparator,用来比较忽略大小写的两个字符串,比较逻辑是,依次取出两个字符进行忽略大小写的比较,其余逻辑和上面类似。

      hashCode()方法返回String的hash值。

    3. startWith()、endWith()

    startsWith(String prefix)是判断字符串是不是以某个指定的子字符串开始,返回boolean值。

    startsWith(String prefix, int toffset)是判断字符串从指定的位置开始是否是以指定的字符串开始,返回boolean值。其实现逻辑是取出对于位置的两个字符,进行判断。

    endsWith(String suffix)是判断字符串是不是以某个字符串结尾。它的实现逻辑是调用startsWith(String prefix, int toffset)方法,具体实现如下:

     public boolean endsWith(String suffix) {
            return startsWith(suffix, value.length - suffix.value.length);
        }

    4. indexOf()、lastIndexOf()

    indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引。

    indexOf(int ch, int fromIndex):从指定的索引开始搜索,返回在此字符串中第一次出现指定字符处的索引。

    lastIndexOf(int ch):返回最后一次出现的指定字符在此字符串中的索引。

    lastIndexOf(int ch, int fromIndex):从指定的索引开始搜索,返回在此字符串中最后一次出现指定字符处的索引。

    indexOf(String str):返回第一次出现的指定子字符串在此字符串中的索引。

    indexOf(String str, int fromIndex):从指定的索引处开始,返回第一次出现的指定子字符串在此字符串中的索引。

    lastIndexOf(String str):返回在此字符串中最右边出现的指定子字符串的索引。

    lastIndexOf(String str, int fromIndex):从指定的索引处开始向后搜索,返回在此字符串中最后一次出现的指定子字符串的索引。

    以上方法如果没有找到索引,则返回-1.

    5. substring()、concat()、matches()、contains()

    substring(int beginIndex):返回一个新的字符串,它是此字符串的一个子字符串。该子字符串始于指定索引处的字符,一直到此字符串末尾。

    substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的beginIndex处开始,一直到索引endIndex - 1处的字符。

    concat(String str):将指定字符串联到此字符串的结尾。

    matches(String regex):判断此字符串是否匹配给定的正则表达式。

    contains(CharSequence s):判断字符串中是否有该字符序列。

    6. replace()、replaceFirst()、replaceAll()

    replace(char oldChar, char newChar):返回一个新的字符串,用newChar替换此字符串中出现的所有oldChar。具体实现如下:  

    复制代码
    public String replace(char oldChar, char newChar) {
            if (oldChar != newChar) {
                int len = value.length;
                int i = -1;
                char[] val = value; /* avoid getfield opcode */
    
                while (++i < len) {
                    if (val[i] == oldChar) {
                        break;
                    }
                }
                if (i < len) {
                    char buf[] = new char[len];
                    for (int j = 0; j < i; j++) {
                        buf[j] = val[j];
                    }
                    while (i < len) {
                        char c = val[i];
                        buf[i] = (c == oldChar) ? newChar : c;
                        i++;
                    }
                    return new String(buf, true);
                }
            }
            return this;
        }
    复制代码

    replaceFirst(String regex,String replacement):使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的第一个子字符串。

    replaceAll(String regex,String replacement):使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。

    7. split()、join()

      String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串。数控制模式应用的次数,因此影响结果数组的长度。如果该限制n大于 0,则模式将被最多应用n- 1 次,数组的长度将不会大于n,而且数组的最后项将包含超出最后匹配的定界符的所有输入。如果n为非正,则模式将被应用尽可能多的次数,而且数组可以是任意长度。如果n为零,则模式将被应用尽可能多的次数,数组可有任何长度,并且结尾空字符串将被丢弃。

      String[] split(String regex):根据给定的正则表达式的匹配来拆分此字符串。该方法的作用就像是使用给定的表达式和限制参数 0 来调用两参数split 方法。

    复制代码
     public String[] split(String regex, int limit) {
            /* fastpath if the regex is a
             (1)one-char String and this character is not one of the
                RegEx's meta characters ".$|()[{^?*+\", or
             (2)two-char String and the first char is the backslash and
                the second is not the ascii digit or ascii letter.
             */
            char ch = 0;
            if (((regex.value.length == 1 &&
                 ".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1) ||
                 (regex.length() == 2 &&
                  regex.charAt(0) == '\' &&
                  (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
                  ((ch-'a')|('z'-ch)) < 0 &&
                  ((ch-'A')|('Z'-ch)) < 0)) &&
                (ch < Character.MIN_HIGH_SURROGATE ||
                 ch > Character.MAX_LOW_SURROGATE))
            {
                int off = 0;
                int next = 0;
                boolean limited = limit > 0;
                ArrayList<String> list = new ArrayList<>();
                while ((next = indexOf(ch, off)) != -1) {
                    if (!limited || list.size() < limit - 1) {
                        list.add(substring(off, next));
                        off = next + 1;
                    } else {    // last one
                        //assert (list.size() == limit - 1);
                        list.add(substring(off, value.length));
                        off = value.length;
                        break;
                    }
                }
                // If no match was found, return this
                if (off == 0)
                    return new String[]{this};
    
                // Add remaining segment
                if (!limited || list.size() < limit)
                    list.add(substring(off, value.length));
    
                // Construct result
                int resultSize = list.size();
                if (limit == 0) {
                    while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                        resultSize--;
                    }
                }
                String[] result = new String[resultSize];
                return list.subList(0, resultSize).toArray(result);
            }
            return Pattern.compile(regex).split(this, limit);
        }
    复制代码

      String join(CharSequence delimiter, CharSequence... elements):

      String join(CharSequence delimiter, Iterable<? extends CharSequence> elements):这两个方法是jdk1.8中出现的,类似字符串拼接,不过可以指定连接符delimiter,后面的elements中间使用该连接符连接。具体实现如下:

    复制代码
     public static String join(CharSequence delimiter, CharSequence... elements) {
            Objects.requireNonNull(delimiter);
            Objects.requireNonNull(elements);
            // Number of elements not likely worth Arrays.stream overhead.
            StringJoiner joiner = new StringJoiner(delimiter);
            for (CharSequence cs: elements) {
                joiner.add(cs);//该方法中会将连接符拼接
            }
            return joiner.toString();
        }
    复制代码

    8. 字符串大小写转换:

    toLowerCase():转换为小写

    toUpperCase():转换为大写

    9. 其他方法:

    trim():去掉字符串收尾的空白。

    toCharArray():将此字符串转换为一个新的字符数组。

    valueOf():返回 某参数的字符串表示形式。就是将其他类型转换为String类型的一种方式。

    intern():当调用 intern 方法时,如果池已经包含一个等于此String对象的字符串,则返回池中的字符串。否则,将此String对象添加到池中,并且返回此String对象的引用。

    本文来源:博客园

    作者:旧巷的常青藤

  • 相关阅读:
    【html、CSS、javascript-9】jquery-选择器及过滤器
    【python之路40】Python 作用域
    H5缓存
    解决网络不可用--Using_Service_Workers
    跨域请求CORS
    基于node的websocket示例
    test
    函数节流
    ES6 promise
    web前端免费资源集
  • 原文地址:https://www.cnblogs.com/a1111/p/12816545.html
Copyright © 2011-2022 走看看