【学习笔记】0002. JAVA实现字符串反转(10种方法)

221117更新,突发奇想想试试各种反转方式的效率,调整代码,不断增长待反转的字符串位数。

其中,C2方法率先出局,当字符串长度为几千位时,C2方法内存溢出,注释掉,继续增长测试其他方法;B3方法当字符串长度为几十万位时已经需要10秒以上的时间,为了测更高数量级将其注释。最终测试字符串长度为2000W位的输出结果(代码更新至结尾处):

测试字符串s2长度为:20000000
1.调用了reverseA1方法,耗时:126毫秒
2.调用了reverseA2方法,耗时:89毫秒
3.调用了reverseA3方法,耗时:4339毫秒
4.调用了reverseB1方法,耗时:83毫秒
5.调用了reverseB2方法,耗时:316毫秒
6.调用了reverseB4方法,耗时:110毫秒
7.调用了reverseC1方法,耗时:2135毫秒
8.调用了reverseD1方法,耗时:2017毫秒

进入StringBuilder和StringBuffer的reverse方法实现里看,其方法与B1/B4方法基本是一致的,因此这几种方法的效率差不多;而此处测试时也不存在多线程问题,因此StringBuilder和StringBuffer的reverse两种方法本身效率也基本相当(一般说法是StringBuilder的方法不是线程安全的,所以效率会高于StringBuffer)。


220729更新,新增了方法A3,将原字符串转换为List,利用Collections的reverse方法反转后再拼接返回。修改后的输出结果:

测试字符串为:dt我hgir爱asddd,aabbcc
1.调用了reverseA1方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
2.调用了reverseA2方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
3.调用了reverseA3方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
4.调用了reverseB1方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
5.调用了reverseB2方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
6.调用了reverseB3方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
7.调用了reverseB4方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
8.调用了reverseC1方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
9.调用了reverseC2方法,逆序后的结果为:dddsa爱righ我td,ccbbaa
10.调用了reverseD1方法,逆序后的结果为:dddsa爱righ我td,ccbbaa


220531更新,修改代码逻辑,为了让输出更加可读,输出前将所有方法按方法名排序后输出,修改后的输出结果为:

测试字符串为:thgir,aabbcc
1.调用了reverseA1方法,逆序后的结果为:right,ccbbaa
2.调用了reverseA2方法,逆序后的结果为:right,ccbbaa
3.调用了reverseB1方法,逆序后的结果为:right,ccbbaa
4.调用了reverseB2方法,逆序后的结果为:right,ccbbaa
5.调用了reverseB3方法,逆序后的结果为:right,ccbbaa
6.调用了reverseB4方法,逆序后的结果为:right,ccbbaa
7.调用了reverseC1方法,逆序后的结果为:right,ccbbaa
8.调用了reverseC2方法,逆序后的结果为:right,ccbbaa
9.调用了reverseD1方法,逆序后的结果为:right,ccbbaa


本人自己思考+网络搜罗,分类整理了以下4类9种方法:

A类:使用JAVA原生方法

-A1:使用StringBuffer的reverse方法

-A2:使用StringBuilder的reverse方法

B类:遍历字符串(字符数组)实现

-B1:将字符串转变为字符数组,遍历该数组的一半,依次将头尾开始对应的字符交换

-B2:逆向遍历s,正序拼接出一个新的字符串

-B3:正向遍历s,将取出的字符拼接形成中间字符串,将中间字符串拼接在下一个字符的后面

-B4:从首尾两边同时遍历,交换首尾下标位置的字符(类似于B1)

C类:递归实现

-C1:将字符串二分后前后交换,递归结束条件为字符串长度小于等于1

-C2:思路同B3,结束条件是长度为1

D类:其他实现

-D1:利用栈stack先进后出的原理实现

有些方法有重复和相似的地方,以后如果搜集到不太相同的思路和解法,该帖我还会继续维护~欢迎收藏。如果各位有新的思路也欢迎交流,如果本人有写错的地方也欢迎指出。

最后补上代码和测试。

package cn.daycode.leetcode;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @Description: 字符串逆序DEMO
 * @Author : wqin
 */
public class StringReverseDemo {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        // 拿到StringReverseDemo类下所有方法
        Method[] methods = StringReverseDemo.class.getDeclaredMethods();
        // 为了方便排序,将methods数据转化为methods的List
        List<Method> methodsList = new ArrayList<>(Arrays.asList(methods));
        // 调用List对象的sort方法按method的方法名从小到大排序
        methodsList.sort(Comparator.comparing(Method::getName));
        // 初始字符串长度50位
        String s = "头asdaaasd123414fdafsg%^&*^$%T#Fda123drds*())dasda尾";
        // 常量池长度有限制,这里用String.repeat方法构建长字符串,但其是java11开始支持的,如果你的java版本不够,可自行想其他办法
//        String s1 = s.repeat(30);
        String s2 = s.repeat(400000);
        int i = 1;
//        System.out.println("测试字符串s1为:"+s1);
//        System.out.println("测试字符串s1长度为:"+s1.length());
        System.out.println("测试字符串s2长度为:"+s2.length());
        // 反射取出Reverses类中所有名字带reverse的方法,按方法名排序后,依次调用
        for (Method m : methodsList){
            if (m.getName().contains("reverse")){
                System.out.print(i+++".");
                System.out.print("调用了"+m.getName()+"方法,");
                long start = System.currentTimeMillis();
//                System.out.println("s1逆序后的结果为:"+m.invoke(new StringReverseDemo(),s1));
                m.invoke(new StringReverseDemo(),s2);
                long end = System.currentTimeMillis();
                System.out.println("耗时:"+(end-start)+"毫秒");
            }
        }
    }

    // 方法A1:使用StringBuffer的reverse方法
    private static String reverseA1(String s){
        return new StringBuffer(s).reverse().toString();
    }

    // 方法A2:使用StringBuilder的reverse方法
    private static String reverseA2(String s){
        return new StringBuilder(s).reverse().toString();
    }

    // 方法A3:使用Collections的reverse方法
    private static String reverseA3(String s){
        String[] sArray = s.split("");
        List<String> sList = Arrays.asList(sArray);
        Collections.reverse(sList);
        return String.join("",sList);
    }

    // 方法B1:将字符串转变为字符数组,遍历数组的一半,将头尾开始对应的字符交换
    private static String reverseB1(String s){
        char[] chars = s.toCharArray();
        int length = chars.length-1;

        for (int i = 0; i <= length/2; i++) {
            if(chars[i] != chars[length - i]) {
                chars[i] = (char) (chars[i] ^ chars[length - i]);
                chars[length - i] = (char) (chars[i] ^ chars[length - i]);
                chars[i] = (char) (chars[i] ^ chars[length - i]);
            }
        }

        return String.valueOf(chars);
    }

    // 方法B2:逆向遍历s,正序拼接出一个新的字符串
    private static String reverseB2(String s){
        StringBuffer sb = new StringBuffer("");

        for (int i = s.length()-1; i >= 0 ; i--) {
            sb.append(s.charAt(i));
        }

        return sb.toString();
    }

    // 该方法效率很低,当字符串长度为几十万时,已经要10秒以上,为了测更高数量级,注释掉
    // 方法B3:正向遍历s,将取出的字符拼接形成中间字符串,将中间字符串拼接在下一个字符的后面
//    private static String reverseB3(String s){
//        String str = "";
//
//        for (int i = 0; i < s.length() ; i++) {
//            str = s.charAt(i) + str;
//        }
//
//        return str;
//    }

    // 方法C1:递归,将字符串二分后前后交换,递归结束条件为字符串长度小于等于1
    private static String reverseC1(String s){
        if(s.length() <= 1){
            return s;
        }
        String l = s.substring(0, s.length()/2);
        String r = s.substring(s.length()/2, s.length());
        return reverseC1(r)+reverseC1(l);
    }

    // 该方法在字符串长度为几千的时候就会内存溢出,注释掉
    // 方法C2:递归,思路同B3,结束条件是长度为1
//    private static String reverseC2(String s){
//        if (s.length() <= 1){
//            return s;
//        }
//        return  reverseC2(s.substring(1))+s.charAt(0);
//    }

    // 方法D1:利用栈stack先进后出的原理实现
    private static String reverseD1(String s){
        char[] str = s.toCharArray();
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < str.length; i++)
            stack.push(str[i]);

        StringBuffer sb = new StringBuffer("");
        for (int i = 0; i < str.length; i++)
            sb.append(stack.pop());

        return sb.toString();
    }

    // 方法B4:从首尾两边同时遍历,交换首尾下标位置的字符
    private static String reverseB4(String s){
        char[] chars = s.toCharArray();
        int start = 0;
        int end = chars.length-1;

        while (start < end){
            if(chars[start] != chars[end]) {
                chars[start] = (char) (chars[start] ^ chars[end]);
                chars[end] = (char) (chars[start] ^ chars[end]);
                chars[start] = (char) (chars[start] ^ chars[end]);
            }
            start++;
            end--;
        }

        return String.valueOf(chars);
    }

}

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值