Guava的Strings.repeat
如果要自己实现一个repeat的话,最容易想到的可能会这样(示例不考虑int溢出的情况):
public static String repeat(String string, int count) {
int length = string.length() * count;
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < count; i++) {
sb.append(string);
}
return sb.toString();
}
用图画出来应该是这个样子的:
看到guava的实现,在位运算的时候还是小愣了一下,性能是比上面的实现好多了:
public static String repeat(String string, int count) {
// 先去掉检测相关的判断,只看核心的实现
final int len = string.length();
final long longSize = (long) len * (long) count;
final int size = (int) longSize;
final char[] array = new char[size];
string.getChars(0, len, array, 0);
int n;
for (n = len; n < size - n; n <<= 1) {
System.arraycopy(array, 0, array, n, n);
}
System.arraycopy(array, 0, array, n, size - n);
return new String(array);
}
用图画出来是这个样子的:
重点是这段代码:
for (n = len; n < size - n; n <<= 1) {
System.arraycopy(array, 0, array, n, n);
}
System.arraycopy(array, 0, array, n, size - n);
看下php的内核实现,其实原理一样,就是把现有的字串翻倍,当 [达到一半的长度, count为偶数]
或者 [刚超过一半的长度时, count为奇数]
做最后一次连接,只是写法不太一样:
// sed -n '5012,5058p' /usr/local/src/php-7.0.9/ext/standard/string.c
PHP_FUNCTION(str_repeat)
{
zend_string *input_str; /* Input string */
zend_long mult; /* Multiplier */
zend_string *result; /* Resulting string */
size_t result_len; /* Length of the resulting string */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl", &input_str, &mult) == FAILURE) {
return;
}
if (mult < 0) {
php_error_docref(NULL, E_WARNING, "Second argument has to be greater than or equal to 0");
return;
}
/* Don't waste our time if it's empty */
/* ... or if the multiplier is zero */
if (ZSTR_LEN(input_str) == 0 || mult == 0)
RETURN_EMPTY_STRING();
/* Initialize the result string */
result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
result_len = ZSTR_LEN(input_str) * mult;
/* Heavy optimization for situations where input string is 1 byte long */
if (ZSTR_LEN(input_str) == 1) {
memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
} else {
char *s, *e, *ee;
ptrdiff_t l=0;
memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
s = ZSTR_VAL(result);
e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
ee = ZSTR_VAL(result) + result_len;
while (e<ee) {
l = (e-s) < (ee-e) ? (e-s) : (ee-e);
memmove(e, s, l);
e += l;
}
}
ZSTR_VAL(result)[result_len] = '\0';
RETURN_NEW_STR(result);
}
https://github.com/jonschlinkert/repeat-string/blob/master/index.js