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);
}
}