Java源码学习 String(2)

接上篇Java源码学习 String(1)继续学习。

四、String的其他方法

length() //返回字符串长度

isEmpty() //返回字符串是否为空

charAt(int index) //返回字符串中第(index+1)个字符

char[] toCharArray() //转化成字符数组

trim() //去掉两端空格

toUpperCase() //转化为大写

toLowerCase() //转化为小写

String concat(String str) //拼接字符串

String replace(char oldChar, char newChar) //将字符串中的oldChar字符换成newChar字符

//以上两个方法都使用了String(char[] value, boolean share);

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

boolean contains(CharSequence s) //判断字符串是否包含字符序列s

String[] split(String regex, int limit) //按照字符regex将字符串分成limit份。

String[] split(String regex)
String string = "h,o,l,l,i,s,c,h,u,a,n,g";
String[] splitAll = string.split(",");
String[] splitFive = string.split(",",5);
splitAll =  [h, o, l, l, i, s, c, h, u, a, n, g]  
splitFive =  [h, o, l, l, i,s,c,h,u,a,n,g]

1. getBytes

在创建String的时候,可以使用byte[]数组,将一个字节数组转换成字符串,同样,我们可以将一个字符串转换成字节数组,那么String提供了很多重载的getBytes方法。但是,值得注意的是,在使用这些方法的时候一定要注意编码问题。比如:

String s = "你好,世界!"; 
byte[] bytes = s.getBytes();

这段代码在不同的平台上运行得到结果是不一样的。由于我们没有指定编码方式,所以在该方法对字符串进行编码的时候就会使用系统的默认编码方式,比如在中文操作系统中可能会使用GBK或者GB2312进行编码,在英文操作系统中有可能使用iso-8859-1进行编码。这样写出来的代码就和机器环境有很强的关联性了,所以,为了避免不必要的麻烦,我们要指定编码方式。如使用以下方式:

String s = "你好,世界!"; 
byte[] bytes = s.getBytes("utf-8");

2. 比较方法

boolean equals(Object anObject)boolean contentEquals(StringBuffer sb)boolean contentEquals(CharSequence cs)boolean equalsIgnoreCase(String anotherString)int compareTo(String anotherString)int compareToIgnoreCase(String str)boolean regionMatches(int toffset, String other, int ooffset,int len)  //局部匹配
boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len)   //局部匹配

字符串有一系列方法用于比较两个字符串的关系。 前四个返回boolean的方法很容易理解,前三个比较就是比较String和要比较的目标对象的字符数组的内容,一样就返回true,不一样就返回false,核心代码如下:

int n = value.length;
 while (n-- != 0) {
     if (v1[i] != v2[i])
         return false;
     i++;
 }

v1 v2分别代表String的字符数组和目标对象的字符数组。 第四个和前三个唯一的区别就是他会将两个字符数组的内容都使用toUpperCase方法转换成大写再进行比较,以此来忽略大小写进行比较。相同则返回true,不想同则返回false。

在这里,看到这几个比较的方法代码,有很多编程的技巧我们应该学习。我们看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;
    }

该方法首先判断this == anObject ?,也就是说判断要比较的对象和当前对象是不是同一个对象,如果是直接返回true,如不是再继续比较,然后在判断anObject是不是String类型的,如果不是,直接返回false,如果是再继续比较,到了能终于比较字符数组的时候,他还是先比较了两个数组的长度,不一样直接返回false,一样再逐一比较值。 虽然代码写的内容比较多,但是可以很大程度上提高比较的效率。值得学习~~!!!

contentEquals有两个重载,StringBuffer需要考虑线程安全问题,再加锁之后调用contentEquals((CharSequence) sb)方法。contentEquals((CharSequence) sb)则分两种情况,一种是cs instanceof AbstractStringBuilder,另外一种是参数是String类型。具体比较方式几乎和equals方法类似,先做“宏观”比较,在做“微观”比较。

下面这个是equalsIgnoreCase代码的实现:

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

看到这段代码,眼前为之一亮。使用一个三目运算符和&&操作代替了多个if语句。

3. hashCode

hashCode的实现其实就是使用数学公式:

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

s[i]是string的第i个字符,n是String的长度。那为什么这里用31,而不是其它数呢? 计算机的乘法涉及到移位计算。当一个数乘以2时,就直接拿该数左移一位即可!选择31原因是因为31是一个素数!

质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。

素数在使用的时候有一个作用就是如果我用一个数字来乘以这个素数,那么最终的出来的结果只能被素数本身和被乘数还有1来整除!如:我们选择素数3来做系数,那么3*n只能被3和n或者1来整除,我们可以很容易的通过3n来计算出这个n来。这应该也是一个原因!
在存储数据计算hash地址的时候,我们希望尽量减少有同样的hash地址,所谓“冲突”。如果使用相同hash地址的数据过多,那么这些数据所组成的hash链就更长,从而降低了查询效率!所以在选择系数的时候要选择尽量长的系数并且让乘法尽量不要溢出的系数,因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。

31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化,使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits!

在java乘法中如果数字相乘过大会导致溢出的问题,从而导致数据的丢失.

而31则是素数(质数)而且不是很长的数字,最终它被选择为相乘的系数的原因不过与此!

在Java中,整型数是32位的,也就是说最多有2^32= 4294967296个整数,将任意一个
字符串,经过hashCode计算之后,得到的整数应该在这4294967296数之中。那么,最
多有 4294967297个不同的字符串作hashCode之后,肯定有两个结果是一样的,   
hashCode可以保证相同的字符串的hash值肯定相同,但是,hash值相同并不一定是
value值就相同。

4. substring

public String substring(int beginIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

5. replaceFirst、replaceAll、replace区别

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

6. copyValueOf 和 valueOf

valueOf()有很多种形式的重载,这些方法可以将六种基本数据类型的变量转换成String类型。包括:

public static String valueOf(boolean b) {
      return b ? "true" : "false";
}

public static String valueOf(char c) {
       char data[] = {c};
       return new String(data, true);
}
public static String valueOf(int i) {
      return Integer.toString(i);
}

public static String valueOf(long l) {
     return Long.toString(l);
}

public static String valueOf(float f) {
     return Float.toString(f);
}

public static String valueOf(double d) {
    return Double.toString(d);
}

7. intern()方法

public native String intern();

该方法返回一个字符串对象的内部化引用。 众所周知:String类维护一个初始为空的字符串的对象池,当intern方法被调用时,如果对象池中已经包含这一个相等的字符串对象则返回对象池中的实例,否则添加字符串到对象池并返回该字符串的引用。

我们知道,Java是不支持重载运算符,String的“+”是java中唯一的一个重载运算符,那么java使如何实现这个加号的呢?我们先看一段代码:

public static void main(String[] args) {
    String string="hollis";
    String string2 = string + "chuang";
}

将这段代码反编译

public static void main(String args[]){
   String string = "hollis";
   String string2 = (new StringBuilder(String.valueOf(string))).append("chuang").toString();
}

我们发现,其实String对“+”的支持其实就是使用了StringBuilder以及他的append、toString两个方法。

8. String.valueOf和Integer.toString的区别

我们有三种方式将一个int类型的变量变成呢过String类型,那么他们有什么区别?

int i = 5;
String i1 = "" + i;
String i2 = String.valueOf(i);
String i3 = Integer.toString(i);
1、第三行和第四行没有任何区别,因为String.valueOf(i)也是调用
Integer.toString(i)来实现的。
2、第二行代码其实是String i1 = (new StringBuilder()).append(i).toString();
首先创建一个StringBuilder对象,然后再调用append方法,再调用toString方法。

参考:http://www.cnblogs.com/yy1024/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值