Guava源码学习笔记

时间:2022-01-07 16:52:19

Why Guava?

学习和使用Guava可以使自己的代码变得更加优雅、简洁。
Guava is the open-sourced version of Google's core Java libraries: the core utilities that Googlers use every day in their code.
The Guava utilities have been carefully designed, tested, optimized and used in production at Google.
Know and use the libraries, don’t reinvent the wheel.

Objects类

Objects类有几个比较不错的方法,toString、hashCode和equals方法
测试类
@Data
class Person{
private String name;
private int sex;
private int age;


public Person(String name,int sex,int age) {
this.name = name;
this.sex = sex;
this.age = age;
}


@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("name", this.getName())
.add("sex", this.getSex())
.add("age", this.getAge())
.toString();
}
}

1.重写toString方法,减少if else判空逻辑
public void testToString() {
Person zhangsan = new Person(null, 1, 28);
String nameStr = MoreObjects.toStringHelper(zhangsan).omitNullValues()
.add("name", zhangsan.getName()).add("sex", zhangsan.getSex())
.add("age", zhangsan.getAge()).toString();
assetTrue(nameStr.equals("Person{sex=1, age=28}"));
}


2.获取hash值,null也可以当成数据项
public void testObjectsHashCode() {
int hashCode1 = Objects.hashCode("sting", "tony", null, "vincent");
int hashCode2 = Objects.hashCode("sting", "tony", "vincent", null);
assertTrue(hashCode1 != hashCode2);
}


3.比较对象,可以为空,减少判空逻辑
public void testObjectsEquals() {
assertTrue(Objects.equal(null, null));
assertTrue(Objects.equal("sting", "sting"));
assertTrue(!Objects.equal(null, "test"));
//assertTrue(Objects.equal("stings", "sting"));
}


排序的实现和改进

Person person1 = new Person("zhangsan", 1, 25);
Person person2 = new Person("lisi", 0, 30);
Person person3 = new Person(null, 1, 25);
Person person4 = null;
List<Person> persons = Lists
.newArrayList(person1, person2, person3, person4);

public void testOrderingWithComparisonChain() {
assertTrue(persons.toString().equals("[Person{name=zhangsan, sex=1, age=25}, Person{name=lisi, sex=0, age=30}, Person{sex=1, age=25}, null]"));
System.out.println(persons.toString());
Ordering<Person> personOrdering = new Ordering<Person>() {
@Override
public int compare(Person p1, Person p2) {
return ComparisonChain.start().compare(p1.getAge(), p2.getAge())
.compare(p1.getName(), p2.getName())
.compare(p1.getSex(), p2.getSex()).result();
}
}.nullsFirst();
Collections.sort(persons, personOrdering);
System.out.println(persons.toString());
//Collections.sort(persons, personOrdering.reverse());
}

public void testOrderingWithComparator() {
System.out.println(persons.toString());
Collections.sort(persons, new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return ComparisonChain.start().compare(p1.getAge(), p2.getAge())
.compare(p1.getName(), p2.getName())
.compare(p1.getSex(), p2.getSex()).result();
}
});
System.out.println(persons.toString());
}

CharMatcher

CharMatcher内部的大量实现
BREAKING_WHITESPACE:用于匹配所有的可换行的空白符,如\t,\n,\f,\r
WHITESPACE:用于匹配所有空白字符
ASCII:用于匹配ASCII字符
DIGIT:匹配ASCII数字
JAVA_DIGIT:匹配unicode数字
JAVA_LETTER:匹配字母(含中文)
JAVA_LETTER_OR_DIGIT:匹配字母(含中文)或数字
JAVA_UPPER_CASE:匹配所有大写字符
JAVA_LOWER_CASE:匹配所有小写字符
ANY:用于匹配任意字符
NONE:不匹配所有字符

CharMatcher提供的工厂方法
is(final char match):返回匹配指定字符的Matcher
isNot(final char match):返回不匹配指定字符的Matcher
anyOf(final CharSequence sequence):返回能够匹配sequence中任一字符的Matcher
noneOf(CharSequence sequence):返回能够过滤sequence中任一字符的Matcher
inRange(final char startInclusive, final char endInclusive):返回匹配范围内任意字符的Matcher
forPredicate(final Predicate<? super Character> predicate):返回使用Predicate的apply()判断匹配的Matcher
negate():返回与当前Matcher判断规则相反的Matcher
and(CharMatcher other):返回与other匹配条件组合进行与运算的Matcher
or(CharMatcher other):返回与other匹配条件组合进行或运算的Matcher

对匹配字符的操作
获取的符合规则的Matcher后,有以下常用方法来处理字符串并返回结果
removeFrom(CharSequence sequence):去除匹配到的字符
retainFrom(CharSequence sequence):筛选匹配到的字符
replaceFrom(CharSequence sequence, char replacement):使用指定字符替换匹配到的字符
replaceFrom(CharSequence sequence, CharSequence replacement):使用指定字符替换匹配到的字符
trimFrom(CharSequence sequence):去除首尾匹配到的字符
trimLeadingFrom(CharSequence sequence):去除首部匹配到的字符
trimTrailingFrom(CharSequence sequence):去除尾部匹配到的字符
collapseFrom(CharSequence sequence, char replacement):将匹配到的字符组(多个字符)替换成指定字符
trimAndCollapseFrom(CharSequence sequence, char replacement):去除首尾空格后进行字符替换

判定型方法
matchesAnyOf(CharSequence sequence):如果sequence中任一字符匹配,返回true
matchesAllOf(CharSequence sequence):如果sequence中所有字符都匹配,返回true
matchesNoneOf(CharSequence sequence):如果sequence中所有字符都不匹配,返回true

获取字符索引的方法
indexIn(CharSequence sequence):返回匹配到的第一个字符的索引
indexIn(CharSequence sequence, int start):返回从指定索引开始,匹配到的第一个字符的索引
lastIndexIn(CharSequence sequence):返回匹配到的最后一个字符的索引
countIn(CharSequence sequence):返回匹配到的字符数量

@Test
public void testAnyOf() {
assertTrue(CharMatcher.anyOf("gZ").matchesAnyOf("anything"));
}

@Test
public void testNoneOf() {
assertTrue(CharMatcher.noneOf("xZ").matchesAnyOf("anything"));
}

@Test
public void testMatchAny() {
assertTrue(CharMatcher.ANY.matchesAllOf("anything"));
}

@Test
public void testMatchAllOf() {
assertTrue(CharMatcher.BREAKING_WHITESPACE.matchesAllOf("\r\n\r\n"));
}

@Test
public void testMatchDigits() {
assertTrue(CharMatcher.JAVA_DIGIT.matchesAllOf("1231212"));
}

@Test
public void testRetainFrom() {
assertTrue(CharMatcher.DIGIT.retainFrom("Hello 1234 567").equals("1234567"));
}

@Test
public void testRetailFrom() {
System.out.println(CharMatcher.DIGIT.or(CharMatcher.WHITESPACE).retainFrom("Hel**lo 1234 567"));
assertTrue(CharMatcher.DIGIT.or(CharMatcher.WHITESPACE).retainFrom("Hel**lo 1234 567").equals(" 1234 567"));
}

@Test
public void testCollapseFrom() {
assertTrue(CharMatcher.DIGIT.collapseFrom("Hello 1234 567", 'x').equals("Hello x x"));
}

@Test
public void testReplaceFrom() {
assertTrue(CharMatcher.DIGIT.replaceFrom("Hello 1234 567", 'x').equals("Hello xxxx xxx"));
}

@Test
public void testCountIn() {
assertTrue(CharMatcher.DIGIT.countIn("Hello 1234 567") == 7);
}

@Test
public void testIndexIn() {
assertTrue(CharMatcher.WHITESPACE.indexIn("Hello 1234 567") == 5);
}

@Test
public void testLastIndexIn() {
assertTrue(CharMatcher.WHITESPACE.lastIndexIn("Hello 1234 567") == 10);
}

@Test
public void testRemoveFrom() {
assertTrue(CharMatcher.inRange('3', '6').removeFrom("Hello 1234 567").equals("Hello 12 7"));
}

@Test
public void testInRangeNegate() {
assertTrue(CharMatcher.inRange('3', '6').negate().removeFrom("Hello 1234 567").equals("3456"));
}

b.部分源码
public static CharMatcher anyOf(final CharSequence sequence) {
switch (sequence.length()) {
case 0:
return NONE;
case 1:
return is(sequence.charAt(0));
case 2:
final char match1 = sequence.charAt(0);
final char match2 = sequence.charAt(1);
return new CharMatcher() {
@Override public boolean matches(char c) {
return c == match1 || c == match2;
}

@Override public CharMatcher precomputed() {
return this;
}
};
}

final char[] chars = sequence.toString().toCharArray();
Arrays.sort(chars); // not worth collapsing duplicates

return new CharMatcher() {
@Override public boolean matches(char c) {
return Arrays.binarySearch(chars, c) >= 0;
}
};
}

public static final CharMatcher JAVA_DIGIT = new CharMatcher() {
@Override public boolean matches(char c) {
return Character.isDigit(c);
}
};

public static final CharMatcher JAVA_ISO_CONTROL =
inRange('\u0000', '\u001f').or(inRange('\u007f', '\u009f'));

使用Predicate实现集合过滤功能

//用来过滤符合条件的元素
Person person1 = new Person("zhangsan", 1, 25);
Person person2 = new Person("lisi", 0, 30);
Person person3 = new Person(null, 1, 25);
Person person4 = null;
List<Person> persons = Lists
.newArrayList(person1, person2, person3, person4);

public void testPredicte() {
Iterable<Person> personsIter = Iterables
.filter(persons, new Predicate<Person>() {
public boolean apply(Person input) {
return input != null && input.getAge() > 18;
}
});
System.out.println(personsIter.toString());
Collection<Person> filterCollection = Collections2
.filter(persons, new Predicate<Person>() {
public boolean apply(Person input) {
return input != null && input.getAge() > 18;
}
});

System.out.println(filterCollection.toString());


}

public void testPredicates() {
List<String> colors = Lists
.newArrayList("red", "blue", "green", "purple", "yellow",
"pink", "", null);
Iterable<String> colorIter = Iterables.filter(colors, Predicates
.and(Predicates.<String>notNull(),
Predicates.containsPattern("pu")));
System.out.println(colorIter.toString());
}

使用Function实现集合转换功能

//将对象集合转换为字符串集合。
public void testFunction() {
Collection<String> filterCollection = Collections2
.transform(persons, new Function<Person, String>() {
public String apply(Person person) {
return null == person ?
"" :
null == person.getName() ?
"" :
person.getName();
}
});
List<String> names = Lists.newArrayList(filterCollection);
System.out.println(names.toString());
}

//过滤空对象和空的对象字段,实现复函数类似的功能
public void testFunctions() {
Function<Person, String> getNameFunction = new Function<Person, String>() {
public String apply(Person person) {
return null == person.getName() ? "" : person.getName();
}
};
Predicate<CharSequence> strFilter = Predicates.containsPattern("li");
ImmutableList<String> names = FluentIterable.from(persons)
.filter(Predicates.<Person>notNull()).transform(getNameFunction)
.filter(strFilter).toList();
System.out.println(names.toString());
}

像Lists和Maps这类的Collection工具类给我们提供了转换的方法:
topMap = Maps.transformValues(fromMap, function);
toList = Lists.transform(fromList, function);
public void testMapsTransformFromValues() {
Map<String, Integer> rmbNameMoneyMapper = ImmutableMap.of("zhangsan", 100, "lisi", 80, "wangwu", 40);
System.out.println(Joiner.on('|').withKeyValueSeparator(':').join(rmbNameMoneyMapper));
Map<String, Double> dolorNameMoneyMapper = Maps.transformValues(rmbNameMoneyMapper, new Function<Integer, Double>() {
public Double apply(Integer input) {
if(input == null) {
return -1.0;
}
return input / 6.67;
}
});
System.out.println(Joiner.on('|').withKeyValueSeparator(':').join(dolorNameMoneyMapper));
}
结果:
//zhangsan:100|lisi:80|wangwu:40
//zhangsan:14.992503748125937|lisi:11.99400299850075|wangwu:5.997001499250375

public void testListsTransformFrom() {
List<Double> rmbMoneyList = Lists.newArrayList(100.9, 80.0, 40.0, 20.9);
System.out.println(Joiner.on(',').skipNulls().join(rmbMoneyList));
List<Double> dollarMoneyList = Lists.transform(rmbMoneyList, new Function<Double, Double>() {
public Double apply(Double input) {
return input / 6.67;
}
});
System.out.println(Joiner.on(',').skipNulls().join(dollarMoneyList));
}
结果:
//100.9,80.0,40.0,20.9
//15.12743628185907,11.99400299850075,5.997001499250375,3.1334332833583205

使用Joinner拼接字符串

//测试Joinner类
public void testJoinner() {
List<String> colors = Lists.newArrayList("red", "blue", "green", "purple", "yellow", "pink", "", null);
// String colorStr = Joiner.on(',').useForNull("no color").skipNulls().join(colors);
String colorStr = Joiner.on(',').useForNull("no color").join(colors);
System.out.println(colorStr);
}

public void testMapJoinner() {
Map<String,String> cityDistMapper = ImmutableMap.of("海淀区", "北京", "朝阳区", "北京", "昌平区", "北京");
String cityDistMapperStr = Joiner.on("|").withKeyValueSeparator("-").join(cityDistMapper);
System.out.println(cityDistMapperStr);
}

//Joinner部分源码
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
checkNotNull(appendable);
if (parts.hasNext()) {
appendable.append(toString(parts.next()));
while (parts.hasNext()) {
appendable.append(separator);
appendable.append(toString(parts.next()));
}
}
return appendable;
}

//优雅避免,用自有的toString方法可方便进行useForNull,否则会出现s异常
@CheckReturnValue
public Joiner useForNull(final String nullText) {
checkNotNull(nullText);
return new Joiner(this) {
@Override
CharSequence toString(@Nullable Object part) {
return (part == null) ? nullText : Joiner.this.toString(part);
}
@Override
public Joiner useForNull(String nullText) {
throw new UnsupportedOperationException("already specified useForNull");
}

@Override
public Joiner skipNulls() {
throw new UnsupportedOperationException("already specified useForNull");
}
};
}

@CheckReturnValue
public Joiner skipNulls() {
return new Joiner(this) {
@Override
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
checkNotNull(appendable, "appendable");
checkNotNull(parts, "parts");
while (parts.hasNext()) {
Object part = parts.next();
if (part != null) {
appendable.append(Joiner.this.toString(part));
break;
}
}
while (parts.hasNext()) {
Object part = parts.next();
if (part != null) {
appendable.append(separator);
appendable.append(Joiner.this.toString(part));
}
}
return appendable;
}

@Override
public Joiner useForNull(String nullText) {
throw new UnsupportedOperationException("already specified skipNulls");
}

@Override
public MapJoiner withKeyValueSeparator(String kvs) {
throw new UnsupportedOperationException("can't use .skipNulls() with maps");
}
};


}

使用Splitter拆分字符串

//Splitter类测试代码
public void testMapSppliter() {
Map<String,String> cityDistMapper = Maps.newHashMap();
String beijingDistricts = " 海淀区:北京| 朝阳区:北京| 东城区:北京 ||西城区:北京|昌平区:北京 | |";
cityDistMapper.putAll(Splitter.on("|").omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(beijingDistricts));
System.out.println(cityDistMapper.entrySet().toString());
}

public void testSppliter() {
String colorStrs = "red,blue ,green, purple, yellow ,pink, , ,";
List<String> colors = Lists.newArrayList(Splitter.on(',').omitEmptyStrings().trimResults().split(colorStrs));
System.out.println(colors.toString());


//Splitter部分源码
@CheckReturnValue
public static Splitter on(final String separator) {
checkArgument(separator.length() != 0, "The separator may not be the empty string.");
if (separator.length() == 1) {
return Splitter.on(separator.charAt(0));
}

return new Splitter(
new Strategy() {
@Override
public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) {
return new SplittingIterator(splitter, toSplit) {
@Override
public int separatorStart(int start) {
int separatorLength = separator.length();

positions:
for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) {
for (int i = 0; i < separatorLength; i++) {
if (toSplit.charAt(i + p) != separator.charAt(i)) {
continue positions;
}
}
return p;
}
return -1;
}

@Override
public int separatorEnd(int separatorPosition) {
return separatorPosition + separator.length();
}
};
}
});
}


@Override
protected String computeNext() {
/*
* The returned string will be from the end of the last match to the
* beginning of the next one. nextStart is the start position of the
* returned substring, while offset is the place to start looking for a
* separator.
*/

int nextStart = offset;
while (offset != -1) {
int start = nextStart;
int end;
int separatorPosition = separatorStart(offset);
if (separatorPosition == -1) {
end = toSplit.length();
offset = -1;
} else {
end = separatorPosition;
offset = separatorEnd(separatorPosition);
}

if (offset == nextStart) {
/*
* This occurs when some pattern has an empty match, even if it
* doesn't match the empty string -- for example, if it requires
* lookahead or the like. The offset must be increased to look for
* separators beyond this point, without changing the start position
* of the next returned substring -- so nextStart stays the same.
*/
offset++;
if (offset >= toSplit.length()) {
offset = -1;
}
continue;
}

while (start < end && trimmer.matches(toSplit.charAt(start))) {
start++;
}
while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
end--;
}

if (omitEmptyStrings && start == end) {
// Don't include the (unused) separator in next split string.
nextStart = offset;
continue;
}

if (limit == 1) {
// The limit has been reached, return the rest of the string as the
// final item. This is tested after empty string removal so that
// empty strings do not count towards the limit.
end = toSplit.length();
offset = -1;

// Since we may have changed the end, we need to trim it again.
while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
end--;
}
} else {
limit--;
}
return toSplit.subSequence(start, end).toString();
}
return endOfData();
}

MultiSet和MultiMap的使用场景

a.MultiMap的使用,主要用来统计
public void testHashMultiMap() {
Multimap<String, String> multimap = HashMultimap.create();
String beijingDistricts = "海淀区| 海淀区 | 朝阳区|东城区 ||西城区|昌平区";
String shanghaiDistricts = "静安区 |徐汇区| | 浦东区|普陀区| |崇明区";
multimap.putAll("北京", Splitter.on('|').omitEmptyStrings().trimResults().split(beijingDistricts));
multimap.putAll("上海", Splitter.on('|').omitEmptyStrings().trimResults().split(shanghaiDistricts));
System.out.println(multimap.toString());
assertTrue(multimap.get("北京").toString().equals("[朝阳区, 西城区, 东城区, 海淀区, 昌平区]"));
multimap.remove("北京", "东城区");
assertTrue(multimap.get("北京").toString().equals("[朝阳区, 西城区, 海淀区, 昌平区]"));
}

public void testArrayListMultimap() {
Multimap<String, String> multimap = ArrayListMultimap.create();
String beijingDistricts = "海淀区| 海淀区 | 朝阳区|东城区 ||西城区|昌平区";
String shanghaiDistricts = "静安区 |徐汇区| | 浦东区|普陀区| |崇明区";
multimap.putAll("北京", Splitter.on('|').omitEmptyStrings().trimResults().split(beijingDistricts));
multimap.putAll("上海", Splitter.on('|').omitEmptyStrings().trimResults().split(shanghaiDistricts));
Map<String, Collection<String>> cityDistMapper = multimap.asMap();
System.out.println(multimap.toString());
assertTrue(multimap.get("北京").toString().equals("[海淀区, 海淀区, 朝阳区, 东城区, 西城区, 昌平区]"));
multimap.remove("北京", "东城区");
assertTrue(multimap.get("北京").toString().equals("[海淀区, 海淀区, 朝阳区, 西城区, 昌平区]"));
}

b.MultiSet的使用,主要用来计数
public void testHashMultiSet() {
String colorStr = "red|blue|yellow |green| red|purple|red|yellow|blue |blue|blue|green||blue";
List<String> colorStrs = Lists.newArrayList(Splitter.on('|').omitEmptyStrings().trimResults().split(colorStr));
Multiset<String> countStrs = HashMultiset.create();
countStrs.addAll(colorStrs);
StringBuilder stringBuilder = new StringBuilder("");
for (String color : countStrs.elementSet()) {
stringBuilder.append(color + ":" + countStrs.count(color) + "|");
}
assertTrue(stringBuilder.toString().equals("red:3|purple:1|blue:5|green:2|yellow:2|"));
}

几个常用工具类

1.lists和maps操作
ImmutableList<String> of = ImmutableList.of("a", "b", "c", "d");
ImmutableMap<String,String> map = ImmutableMap.of("key1", "value1", "key2", "value2");
Lists.newArrayList(),Maps.newHashMap();

2.比较数字
int compare = Ints.compare(a, b);
int compare = Doubles.compare(a,b);

3.数组操作
Ints.toArray();
Ints.asList(numbers);
boolean contains = Ints.contains(array, a);
int indexOf = Ints.indexOf(array, a);
int max = Ints.max(array);
int min = Ints.min(array);
int[] concat = Ints.concat(array, array2);

4.Iterables,Collections2
Collections2的filter、transform方法
Iterables的filter、transform等方法
Iterables.all(list,predicateObject)

5.使用Guava的选择和预判断使得代码更简洁,过多的if else return使代码看起来很不美观。
public void doSomething( List<Object> list ) {
if( list == null ) {
throw new IllegalArgumentException( "List must not be null" );
}
if( list.isEmpty() ) {
throw new IllegalArgumentException( "List must not be empty" );
}
doSomethingMore( list );
}
使用Guava的预判断后的代码,参数有问题尽快失败。
public void doSomething( List<Object> list ) {
checkArgument( list != null, "List must not be null" );
checkArgument( !list.isEmpty(), "List must not be empty" );
doSomethingMore( list );
}

1).checkArgument(boolean) :
功能描述:检查boolean是否为真。 用作方法中检查参数
失败时抛出的异常类型: IllegalArgumentException

2).checkNotNull(T):
功能描述:检查value不为null, 直接返回value;
失败时抛出的异常类型:NullPointerException

3).checkState(boolean):
功能描述:检查对象的一些状态,不依赖方法参数。 例如, Iterator可以用来next是否在remove之前被调用。
失败时抛出的异常类型:IllegalStateException

public void testCheckArgument() {
Preconditions.checkArgument(false);
}

public void testCheckArgumentWithMessage() {
Preconditions.checkArgument(false, "hello");
}

public void testCheckState_simple_success() {
Preconditions.checkState(true);
}

public void testCheckStateWithMessage() {
Preconditions.checkState(false, "checkState false");
}

private static final String NON_NULL_STRING = "hello world";


public void testCheckNotNull_simple_success() {
String result = Preconditions.checkNotNull(NON_NULL_STRING);
assertSame(NON_NULL_STRING, result);
}

public void testCheckNotNull() {
Preconditions.checkNotNull(null);
}

public void testCheckNotNullWithMessage() {
String nullNameStr = "I'm null";
String result = Preconditions.checkNotNull(null, nullNameStr);
}
6.Strings
nulltoEmpty(),emptyToNull(),isNullOrEmpty(),commonPrefix(),commonSuffix(),
paddingStart(),paddingEnd(),repeat()这几个方法。
public void testPadStart() {
assertEquals("-", Strings.padStart("", 1, '-'));
assertEquals("--", Strings.padStart("", 2, '-'));
assertEquals("-x", Strings.padStart("x", 2, '-'));
assertEquals("--x", Strings.padStart("x", 3, '-'));
assertEquals("-xx", Strings.padStart("xx", 3, '-'));
}

public void testRepeat() {
String input = "20";
assertEquals("", Strings.repeat(input, 0));
assertEquals("20", Strings.repeat(input, 1));
assertEquals("2020", Strings.repeat(input, 2));
assertEquals("202020", Strings.repeat(input, 3));
}

public void testCommonPrefix() {
assertEquals("", Strings.commonPrefix("", "abc"));
assertEquals("", Strings.commonPrefix("xyz", "abcxyz"));
assertEquals("a", Strings.commonPrefix("abc", "aaaaa"));
assertEquals("aa", Strings.commonPrefix("aa", "aaaaa"));
}

public static String repeat(String string, int count) {
checkNotNull(string); // eager for GWT.

if (count <= 1) {
checkArgument(count >= 0, "invalid count: %s", count);
return (count == 0) ? "" : string;
}

// IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark
final int len = string.length();
final long longSize = (long) len * (long) count;
final int size = (int) longSize;
if (size != longSize) {
throw new ArrayIndexOutOfBoundsException("Required array size too large: " + 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);
}