1 package com.wjq.javautils; 2 3 import java.util.Arrays; 4 /** 5 * 6 * @author WangJingQian 7 * 8 */ 9 public class MyString implements java.io.Serializable, Comparable<MyString> { 10 11 private static final long serialVersionUID = 8954832331431321433L; 12 13 // 字符数组 14 private final char[] value; 15 // Default to 0 字符串缓存散列码 16 private int hash; 17 private static final int MAX_CHAR_COUNT = 65535; 18 19 // public MyString() 20 // 无参构造方法 因为MyString对象内容不可变,所以没有必要调用此方法,已省略。 21 22 // public MyString(MyString original) 23 // 创建字符串参数的副本 24 // 理由同上,已省略。 25 26 // 该构造方法创建字符数组参数对应的字符串对象 27 public MyString(char[] value) { 28 this.value = Arrays.copyOf(value, value.length); 29 } 30 31 // 该构造方法创建部分字符数组参数对应的字符串对象 32 // offset是起始位置,count是字符数量 33 public MyString(char[] value, int offset, int count) { 34 // 如果起始位置小于0 35 if (offset < 0) { 36 throw new StringIndexOutOfBoundsException(offset); 37 } 38 39 // 如果字符数量小于1 40 // 非null的空字符串对于MyString来说没有意义 41 if (count <= 0) { 42 throw new StringIndexOutOfBoundsException(count); 43 } 44 45 // Note: offset or count might be near -1>>>1. 46 // 如果起始位置与字符数量不符合逻辑 47 if (offset > value.length - count) { 48 throw new StringIndexOutOfBoundsException(offset + count); 49 } 50 51 // 创建字符数组副本 52 this.value = Arrays.copyOfRange(value, offset, offset + count); 53 } 54 55 // 获取字符串长度 56 public int length() { 57 return value.length; 58 } 59 60 // 判断字符串是否为空 61 public boolean isEmpty() { 62 return value.length == 0; 63 } 64 65 // 按下标获取单个字符 66 public char charAt(int index) { 67 // 避免获取属性操作码 68 char[] val = value; 69 // 如果下标<0或者下标>=字符数组长度 70 if ((index < 0) || (index >= val.length)) { 71 throw new StringIndexOutOfBoundsException(index); 72 } 73 74 return val[index]; 75 } 76 77 // 获取子串,把它拷贝到目标字符数组 78 public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { 79 // 如果源数组开始位置<0 80 if (srcBegin < 0) { 81 throw new StringIndexOutOfBoundsException(srcBegin); 82 } 83 84 char[] val = value; 85 // 如果源数组结束位置>字符数组长度 86 if (srcEnd > val.length) { 87 throw new StringIndexOutOfBoundsException(srcEnd); 88 } 89 // 如果源数组开始位置>=源数组结束位置 90 // 区间前闭后开,说明要拷贝的字符个数<=0 91 if (srcBegin >= srcEnd) { 92 throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); 93 } 94 95 System.arraycopy(val, srcBegin, dst, dstBegin, srcEnd - srcBegin); 96 } 97 98 // 比较两个引用指向的MyString对象内容是否相同 99 @Override 100 public boolean equals(Object anotherObject) { 101 // 如果两个引用指向的是同一个MyString对象 102 if (this == anotherObject) { 103 return true; 104 } 105 106 // 如果第2个引用指向的对象是MyString实例 107 if (anotherObject instanceof MyString) { 108 // 强制类型转换,Object对象没有value属性 109 MyString anotherMyString = (MyString) anotherObject; 110 111 char[] v1 = value; 112 char[] v2 = anotherMyString.value; 113 114 // 获取第1个引用指向的MyString对象的字符串长度 115 int len = v1.length; 116 117 // 如果两个字符串长度相等 118 if (len == v2.length) { 119 // 依次比较字符 120 for (int i = 0; i < len; i++) { 121 if (v1[i] != v2[i]) { 122 return false; 123 } 124 } 125 126 return true; 127 } 128 } 129 130 return false; 131 } 132 133 // 比较两个MyString对象的大小 134 @Override 135 public int compareTo(MyString anotherMyString) { 136 if (anotherMyString == null) { 137 return 1; 138 } 139 140 char[] v1 = value; 141 char[] v2 = anotherMyString.value; 142 143 // 获取字符数组长度 144 int len1 = v1.length; 145 int len2 = v2.length; 146 147 // 获取最小长度 148 int minLen = Math.min(len1, len2); 149 for (int i = 0; i < minLen; i++) { 150 char c1 = v1[i]; 151 char c2 = v2[i]; 152 153 // 如果两个字符不同 154 if (c1 != c2) { 155 return c1 - c2; 156 } 157 } 158 159 // 根据长度比较大小 160 return len1 - len2; 161 } 162 163 // 获取散列码 164 @Override 165 public int hashCode() { 166 int h = hash; 167 char[] val = value; 168 int len = val.length; 169 170 // 如果字符串缓存散列码为0并且字符串数组长度大于0 171 // 由于内容不可变,所以要避免重复计算,只计算一次 172 if (h == 0 && len > 0) { 173 // 遍历每个字符 174 for (int i = 0; i < len; i++) { 175 // 31 * h会被JVM优化成(h << 5) - h 176 h = 31 * h + val[i]; 177 } 178 179 // 保存计算后的字符串缓存散列码 180 hash = h; 181 } 182 183 return h; 184 } 185 186 // 截取字符串,获取子串对象 187 public MyString substring(int beginIndex) { 188 // 如果起始下标<0 189 if (beginIndex < 0) { 190 throw new StringIndexOutOfBoundsException(beginIndex); 191 } 192 193 char[] val = value; 194 // 获取截取长度 195 int subLen = val.length - beginIndex; 196 // 如果截取长度<=0 197 if (subLen <= 0) { 198 throw new StringIndexOutOfBoundsException(subLen); 199 } 200 201 return (beginIndex == 0) ? this : new MyString(val, beginIndex, subLen); 202 } 203 204 // 截取字符串,获取子串对象 205 public MyString substring(int beginIndex, int endIndex) { 206 // 如果起始下标<0 207 if (beginIndex < 0) { 208 throw new StringIndexOutOfBoundsException(beginIndex); 209 } 210 211 char[] val = value; 212 int len = val.length; 213 // 如果末尾下标>字符数组长度 214 if (endIndex > len) { 215 throw new StringIndexOutOfBoundsException(endIndex); 216 } 217 // 获取截取长度 218 int subLen = endIndex - beginIndex; 219 // 如果截取长度<=0 220 if (subLen <= 0) { 221 throw new StringIndexOutOfBoundsException(subLen); 222 } 223 224 return ((beginIndex == 0) && (endIndex == len)) ? this 225 : new MyString(val, beginIndex, subLen); 226 } 227 228 // 获取该字符串对应的字符数组,返回该数组副本 229 public char[] toCharArray() { 230 char[] val = value; 231 int len = val.length; 232 233 // Cannot use Arrays.copyOf because of class initialization order issues 234 // 不能用Arrays的copyOf方法,会出现类初始化顺序问题 235 char result[] = new char[len]; 236 // 创建字符数组副本 237 System.arraycopy(val, 0, result, 0, len); 238 239 return result; 240 } 241 242 // 判断是否包含指定前缀 243 public boolean startsWith(MyString prefix, int beginIndex) { 244 if (prefix == null) { 245 return false; 246 } 247 248 char[] val = value; 249 char[] prefixVal = prefix.value; 250 int len2 = prefixVal.length; 251 // Note: toffset might be near -1>>>1. 252 if ((beginIndex < 0) || (beginIndex + len2 > val.length)) { 253 return false; 254 } 255 256 for (int i = 0; i < len2; i++) { 257 if (val[beginIndex++] != prefixVal[i]) { 258 return false; 259 } 260 } 261 262 return true; 263 } 264 265 // 判断是否包含指定前缀,默认从0开始 266 public boolean startsWith(MyString prefix) { 267 return startsWith(prefix, 0); 268 } 269 270 // 判断是否包含指定后缀,从末尾开始 271 public boolean endsWith(MyString suffix) { 272 return startsWith(suffix, value.length - suffix.value.length); 273 } 274 275 // 获取子串在该字符串中第一次出现的位置 276 public int indexOf(MyString str) { 277 return indexOf(str, 0); 278 } 279 280 private static void setMoveLength(char[] v, int len, int[] moveLength) { 281 // 默认S中的任何字符均不出现在T中,那么每次移动的距离为T的长度 + 1 282 for (int i = 0; i < MAX_CHAR_COUNT; i++) { 283 moveLength[i] = len + 1; 284 } 285 286 // 查找能够出现在T中的字符,若一个字符出现多次,选择最右位置的字符,所以T的下标遍历从0开始 287 for (int i = 0; i < len; i++) { 288 moveLength[v[i]] = len - i; 289 } 290 } 291 292 // 通过Sunday算法获取子串在母串中第一次出现的位置 293 private int indexOfBySunday(char[] v1, int len1, char[] v2, int len2) { 294 int[] moveLength = new int[MAX_CHAR_COUNT]; 295 setMoveLength(v2, len2, moveLength); 296 297 // S遍历下标 298 int i = 0; 299 while (i < len1) { 300 int j = 0; 301 // 符合条件下标就继续右移 302 for (; j < len1 && i + j < len1 && v1[i + j] == v2[j]; j++){} 303 304 // 遍历结束,判断遍历情况 305 if (j >= len2) { 306 return i; 307 } 308 // 查找不成功,那么S下标右移 309 if (i + len2 >= len1) { 310 return -1; 311 } 312 313 i += moveLength[v1[i + len1]]; 314 } 315 316 return -1; 317 } 318 319 // 获取子串在该字符串中从起始位置开始第一次出现的位置 320 public int indexOf(MyString str, int fromIndex) { 321 if (str == null) { 322 return -1; 323 } 324 325 char[] v1 = value; 326 char[] v2 = str.value; 327 328 int len1 = v1.length; 329 int len2 = v2.length; 330 331 // 如果源字符串的开始位置非法或者起始位置<0 332 if (fromIndex < 0 || fromIndex >= len1) { 333 throw new IllegalArgumentException("源字符串相关参数非法"); 334 } 335 // 如果子串是空字符串 336 if (len2 == 0) { 337 return fromIndex; 338 } 339 // 如果子串不可能在当前字符串中出现 340 if (fromIndex + len2 > len1) { 341 return -1; 342 } 343 344 return indexOfBySunday(v1, len1, v2, len2); 345 } 346 347 // 判断当前字符串是否包含子串 348 public boolean contains(MyString myString) { 349 return indexOf(myString) > -1; 350 } 351 }