Java 模仿jdk MyString

Posted wjq2017

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 模仿jdk MyString相关的知识,希望对你有一定的参考价值。

 

  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 }

 

以上是关于Java 模仿jdk MyString的主要内容,如果未能解决你的问题,请参考以下文章

设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

# Java 常用代码片段

# Java 常用代码片段

cisp401系列MyString.java文件

Java 等价于不变文化

JDK9的JShell简单使用