java怎么实现字符串倒叙_改善Java程序的N个建议(三)

本文探讨了Java中String、StringBuffer和StringBuilder的使用场景及性能差异,建议在字符串频繁变动时使用StringBuilder。同时,推荐在复杂字符串操作中利用正则表达式提高效率,例如统计文章中的单词数。通过实例比较了加号、concat和append方法的性能,指出在性能敏感的情况下应选择更优的字符串拼接方式。
摘要由CSDN通过智能技术生成

9a5a11f7187a9e1ee8636df3367be63b.png

今天带来的都是几条和String字符串操作有关的建议,细品、细品。

建议54:正确使用String、StringBuffer、StringBuilder

Java的CharSequence接口有三个实现类与字符串有关:String、StringBuffer、StringBuilder。 String是一个不可变量,也就是当他创建之后就会中内存中永久存在且不能修改,即使通过String自身的方法产生的也是一个新的字符串。

String str = "hello";
String str1 = str.substring(1);

str字符串通过substring方法重新生成了一个str1字符串其值为“ello”,那有没有可能不创建对象返回自己呢?str.substring(0)就不会产生新对象,JVM会从字符串池只能够返回str的引用。

StringBuffer和String一样中内存中保存的都是一个有序的字符序列,不同点是StringBuffer对象的值是可变的,例如:

StringBuffer sb = new StringBuffer("hello");
sb.append(" world");

上面的代码sb的值一直在变化,经过append后变为了“hello world”,那这个和String类通过“+”连接字符串有什么区别呢?

当然有区别,通过String加号连接的字符串,字符串变量指向了新的引用地址,而StringBuffer则不会变更其引用地址。

StringBuilder和StringBuffer基本相同,不同点是,StringBuffer是线程安全的,而StringBuilder是线程不安全的,所以可以看出String类的操作要远慢于StringBuffer和StringBuilder。

弄清楚了三者的原理,再来看看他们的使用场景: String类使用场景:在字符串不经常变化的时候使用,例如声明常量、少量变量等 StringBuffer类使用场景:频繁进行字符串的运算,如拼接、替换、删除等,并且运行在多线程环境中,例如XML解析、HTTP参数解析和封装等 * StringBuilder类使用场景:频繁进行字符串的运算,如拼接、替换、删除等,并且运行在单线程环境中,例如SQL语句的拼装、JSON封装等

建议56:自由选择字符串拼接方法

对于字符串等拼接一般有三种方法:加号、concat方法、StringBuffer或StringBuilder的append方法,那这三者具体有什么区别呢?来看看下面的例子:

str += "a"; // 加号连接
str = str.concat("a");  // concat方法连接

分别用这三种方法做字符串拼接,循环10W次后,检查其执行时间:

public class Proposal_56 {
    public static void doWithAdd() {
        String str = "a";
        for (int i = 0; i < 100000; i++) {
            str += "c";
        }
    }

    public static void doWithConcat() {
        String str = "a";
        for (int i = 0; i < 100000; i++) {
            str = str.concat("c");
        }
    }

    public static void doWithStringBuilder() {
        StringBuilder sb = new StringBuilder("a");
        for (int i = 0; i < 100000; i++) {
            sb.append("c");
        }
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        doWithAdd();
        long endTime = System.currentTimeMillis();
        System.out.println("doWithAdd运行时间:" + (endTime - startTime) + "ms");

        startTime = System.currentTimeMillis();
        doWithConcat();
        endTime = System.currentTimeMillis();
        System.out.println("doWithConcat运行时间:" + (endTime - startTime) + "ms");

        startTime = System.currentTimeMillis();
        doWithStringBuilder();
        endTime = System.currentTimeMillis();
        System.out.println("doWithStringBuilder运行时间:" + (endTime - startTime) + "ms");
    }
}

结果如下:

1ebe3874d80273b17cddcdafd51e12ca.png

1.加号拼接字符串: 编译器对字符串使用加号做了优化,它会使用StringBuilder的append方法进行追加,其效果和下面的代码相同:

str = new StringBuilder(str).append("c").toString();

那按道理,不应该也和StringBuilder的效率一样吗,为什么用加号花了4372ms,而StringBuilder只花了2ms,原因很简答,一它每次循环都会创建一个StringBuilder对象,循环10W次就是10W个对象,二是每次执行完毕调用toString方法,转换成字符串也需要消耗时间。

2.concat方法拼接字符串: 先来看一下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);
}

整体看上去就是一个数组拷贝,虽然这内存中的处理是原子操作,速度非常快,但是注意看最后的return,每次concat方法都会创建一个新的String对象,这就是concat方法慢下来的原因,循环10W次,同样创建来10W个对象。

3.append方法拼接字符串: 同样也先看一下append的源码:

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
}

整个append方法都中做字符数组处理,加长,然后数组拷贝,这些都是基本的数据操作,没有新建任何对象,所以速度也就快来。

这三种拼接字符串的方法,功能相同,性能各不相同,但并不表示我们一定要使用StringBuilder,这是因为“+”非常符合我们但编程习惯,便于阅读,在大多数情况用加号即可,只有在系统性能临界的时候才考虑concat或append方法。

建议57:推荐在复杂字符串操作中使用正则表达式

在日常字符串的操作中经常会用到诸如追加、合并、替换、倒叙、分割等操作,而且Java也为我们提供了append、replace、reverse、split等方法,但是更多的时候,我们还是需要借助正则表达式完成复杂的处理,下面这个例子,统计一篇文章中的英语单词数量,代码如下:

public class Proposal_57 {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            String str = scan.nextLine();
            int wordsCount = str.split(" ").length;
            System.out.println(str + " 单词数:" + wordsCount);
        }
    }
}

返回结果如下:

fd1d9f4cb5dabd874edf997aa5e67096.png

我们发现除了第一条正确外其他都错了,第二条没有考虑用户输入都连续空格,第三条没有考虑连续都单词,第四条没有把连写符“'”考虑进去。那该如何处理呢?我们考虑使用正则表达式:

public class Proposal_57 {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            String str = scan.nextLine();
//          int wordsCount = str.split(" ").length;
            Pattern pattern = Pattern.compile("bw+b");
            Matcher matcher = pattern.matcher(str);
            int wordsCount = 0;
            while (matcher.find()) {
                wordsCount++;
            }
            System.out.println(str + " 单词数:" + wordsCount);
        }
    }
}

改成上述代码之后,得到了下面都结果:

43e91d9b8a866f3ea3bee2a996d4bcff.png

此时所有的结果都正确,b表示单词边界,w表示数字或者字符,这样匹配出来的都将会是有效都代码。正则表达式都字符串匹配可以应用在很多场合,比如常见的服务器日志分析等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值