源码解读之调换字符串顺序——StringBuilder reverse()

来源:互联网 发布:三国志10武将资料数据 编辑:程序博客网 时间:2024/05/22 12:33

1.前言:

今天逛CSDN看见了骆昊的《Java面试题全集(上)》其中第39条:如何实现字符串的反转和替换,如下图所示:

如何反转字符串











其中说到可以使用String或StringBuffer/StringBuilder中的方法。StringBuffer和StringBuilder都继承了AbstractStringBuilder抽象类,该类提供了一个reverse()方法

现在我们就来具体看一下reverse方法

先上源码,如下:

    /**     * Causes this character sequence to be replaced by the reverse of     * the sequence. If there are any surrogate pairs included in the     * sequence, these are treated as single characters for the     * reverse operation. Thus, the order of the high-low surrogates     * is never reversed.     *     * Let n be the character length of this character sequence     * (not the length in {@code char} values) just prior to     * execution of the {@code reverse} method. Then the     * character at index k in the new character sequence is     * equal to the character at index n-k-1 in the old     * character sequence.     *     * 

Note that the reverse operation may result in producing * surrogate pairs that were unpaired low-surrogates and * high-surrogates before the operation. For example, reversing * "\u005CuDC00\u005CuD800" produces "\u005CuD800\u005CuDC00" which is * a valid surrogate pair. * * @return a reference to this object. */ public AbstractStringBuilder reverse() { boolean hasSurrogates = false; int n = count - 1; for (int j = (n-1) >> 1; j >= 0; j--) { int k = n - j; char cj = value[j]; char ck = value[k]; value[j] = ck; value[k] = cj; if (Character.isSurrogate(cj) || Character.isSurrogate(ck)) { hasSurrogates = true; } } if (hasSurrogates) { reverseAllValidSurrogatePairs(); } return this; } /** Outlined helper method for reverse() */ private void reverseAllValidSurrogatePairs() { for (int i = 0; i < count - 1; i++) { char c2 = value[i]; if (Character.isLowSurrogate(c2)) { char c1 = value[i + 1]; if (Character.isHighSurrogate(c1)) { value[i++] = c1; value[i] = c2; } } } }

首先我们看一下第一个,也就是主要的reverse方法

    public AbstractStringBuilder reverse() {        boolean hasSurrogates = false;        int n = count - 1;        for (int j = (n-1) >> 1; j >= 0; j--) {            int k = n - j;            char cj = value[j];            char ck = value[k];            value[j] = ck;            value[k] = cj;            if (Character.isSurrogate(cj) ||                Character.isSurrogate(ck)) {                hasSurrogates = true;            }        }        if (hasSurrogates) {            reverseAllValidSurrogatePairs();        }        return this;    }

第一行

    boolean hasSurrogates = false;
结合

        if (Character.isSurrogate(cj) ||            Character.isSurrogate(ck)) {            hasSurrogates = true;        }                if (hasSurrogates) {            reverseAllValidSurrogatePairs();        }

主要是为了实现特殊字符的处理。这一块就不详细说了,可自行回去看一下Character.isSurrogate()方法

接下来我们来具体看一下实现反转字符串的核心代码

        int n = count - 1;        for (int j = (n-1) >> 1; j >= 0; j--) {            int k = n - j;            char cj = value[j];            char ck = value[k];            value[j] = ck;            value[k] = cj;            if (Character.isSurrogate(cj) ||                Character.isSurrogate(ck)) {                hasSurrogates = true;            }        }
其中count为字符串的长度,value为char数组,按顺序存放了字符串的所有字符

下面就是具体的实现的原理

    //比如我们有一个数组如下    char[] arr = {1,2,3,4,5,6,7,8,9};    //5是中间数,所以位置不用动。将4和6调换位置,将3和7调换位置,将2和8调换位置,将1和9调换位置。这样就实现了顺序的调转     //这边又是如何找到中间数的呢,看下面的三行代码(只是截取片段,所以不标准)    int n = count - 1    for (int j = (n-1) >> 1; j >= 0; j--) {        int k = n - j;    //第一行代码,获取数组下标最大值    /**     * 第二行:int j = (n-1) >> 1;     * 我们以前如何获取需要调换的两个数?比如有10个数,     * 第一种:判断数数量能不能被2整除,即n%2==0。如果可以,需要调换的两个数就是n/2和n+1-(n/2)。如果不行则左边的数为(n-1)/2,右边的为n+1-(n-1)/2     * 第二种:直接用int的特性,左边的数(int)(n/2),右边n+1-((int)(n/2))     * 第三种就是位移,n >> 1是什么意思?我们换成第一种写法说明一下位移运算大家就知道了     * 比如这样一个位移表达式 n >> x,把一个数n右位移x位     * 转换后就是(n-n%2^x)/2^x,这时候如果像右位移1位则变为(n-n%2)/2     * 这时候就比较清楚了:(n-n%2)能被2整除就取n,不能整除就取n-1     * 然后除以2得到的就是左边的数,右边的数就是n+1-左边     * 因为数组下标从0开始,我们刚刚所说的都是从1开始,所以整体减1     * 所以左边边:j = (n-1) >> 1 ; 右边  k = n - j;      *      */

剩下的元素交换顺序我就不说了,就是交换位置。


最后改造一下,封装成自己的工具类,不需要使用AbstractStringBuilder,直接调用方法,传入字符串就可以

public class Reversal {    public static String reverse(String str) {if(str == null || str.length()<=1)return str;char[] value = str.toCharArray();int count = value.length;        boolean hasSurrogates = false;        int n = count - 1;        for (int j = (n-1) >> 1; j >= 0; j--) {            int k = n - j;            char cj = value[j];            char ck = value[k];            value[j] = ck;            value[k] = cj;            if (Character.isSurrogate(cj) ||                Character.isSurrogate(ck)) {                hasSurrogates = true;            }        }        if (hasSurrogates) {            reverseAllValidSurrogatePairs(count, value);        }        return String.copyValueOf(value);    }    /** Outlined helper method for reverse() */    private static void reverseAllValidSurrogatePairs(int count, char[] value) {        for (int i = 0; i < count - 1; i++) {            char c2 = value[i];            if (Character.isLowSurrogate(c2)) {                char c1 = value[i + 1];                if (Character.isHighSurrogate(c1)) {                    value[i++] = c1;                    value[i] = c2;                }            }        }    }    public static void main(String[] args) {String str = "abcdefghijk你好。:‘,./;lmnopqrstuvwxy";System.out.println(reverse(str));    }}


时间也比较赶,也是刚开始写博客,写的不好的地方请大家见谅。

原创粉丝点击