我正在寻找一个简单的公共方法或操作符,允许我重复一些字符串n次。我知道我可以使用for循环来写这个,但我希望在必要时避免for循环,并且应该在某个地方存在一个简单的直接方法。
String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");
相关:
重复字符串javascript
通过重复给定次数的另一个字符串创建NSString
编辑
当它们不是完全必要的时候,我尽量避免使用for循环,因为:
They add to the number of lines of code even if they are tucked away in another function.
Someone reading my code has to figure out what I am doing in that for loop. Even if it is commented and has meaningful variables names, they still have to make sure it is not doing anything "clever".
Programmers love to put clever things in for loops, even if I write it to "only do what it is intended to do", that does not preclude someone coming along and adding some additional clever "fix".
They are very often easy to get wrong. For loops involving indexes tend to generate off by one bugs.
For loops often reuse the same variables, increasing the chance of really hard to find scoping bugs.
For loops increase the number of places a bug hunter has to look.
只使用JRE类(System.arraycopy)并尽量减少临时对象的数量,你可以这样写:
public static String repeat(String toRepeat, int times) {
if (toRepeat == null) {
toRepeat = "";
}
if (times < 0) {
times = 0;
}
final int length = toRepeat.length();
final int total = length * times;
final char[] src = toRepeat.toCharArray();
char[] dst = new char[total];
for (int i = 0; i < total; i += length) {
System.arraycopy(src, 0, dst, i, length);
}
return String.copyValueOf(dst);
}
EDIT
如果没有循环,你可以尝试:
public static String repeat2(String toRepeat, int times) {
if (toRepeat == null) {
toRepeat = "";
}
if (times < 0) {
times = 0;
}
String[] copies = new String[times];
Arrays.fill(copies, toRepeat);
return Arrays.toString(copies).
replace("[", "").
replace("]", "").
replaceAll(", ", "");
}
编辑2
使用Collections甚至更短:
public static String repeat3(String toRepeat, int times) {
return Collections.nCopies(times, toRepeat).
toString().
replace("[", "").
replace("]", "").
replaceAll(", ", "");
}
然而,我仍然喜欢第一个版本。
尽管您不希望使用循环,但我认为您应该使用循环。
String repeatString(String s, int repetitions)
{
if(repetitions < 0) throw SomeException();
else if(s == null) return null;
StringBuilder stringBuilder = new StringBuilder(s.length() * repetitions);
for(int i = 0; i < repetitions; i++)
stringBuilder.append(s);
return stringBuilder.toString();
}
你不使用for循环的理由并不充分。针对你的批评:
Whatever solution you use will almost certainly be longer than this. Using a pre-built function only tucks it under more covers.
Someone reading your code will have to figure out what you're doing in that non-for-loop. Given that a for-loop is the idiomatic way to do this, it would be much easier to figure out if you did it with a for loop.
Yes someone might add something clever, but by avoiding a for loop you are doing something clever. That's like shooting yourself in the foot intentionally to avoid shooting yourself in the foot by accident.
Off-by-one errors are also mind-numbingly easy to catch with a single test. Given that you should be testing your code, an off-by-one error should be easy to fix and catch. And it's worth noting: the code above does not contain an off-by-one error. For loops are equally easy to get right.
So don't reuse variables. That's not the for-loop's fault.
Again, so does whatever solution you use. And as I noted before; a bug hunter will probably be expecting you to do this with a for loop, so they'll have an easier time finding it if you use a for loop.
所以你想避免循环?
现在你知道了:
public static String repeat(String s, int times) {
if (times <= 0) return "";
else return s + repeat(s, times-1);
}
(当然我知道这是丑陋和低效的,但它没有循环:-p)
你想要更简单更漂亮吗?使用jython:
s * 3
编辑:让我们优化一下:-D
public static String repeat(String s, int times) {
if (times <= 0) return "";
else if (times % 2 == 0) return repeat(s+s, times/2);
else return s + repeat(s+s, times/2);
}
Edit2:我已经为4个主要的替代方案做了一个快速而粗略的基准测试,但我没有时间多次运行它来获得几个输入的平均值和绘制时间……如果有人想试试,下面是代码:
public class Repeat {
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
String s = args[1];
int l = s.length();
long start, end;
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatLog2(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("RecLog2Concat: " + (end-start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatR(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("RecLinConcat: " + (end-start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatIc(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("IterConcat: " + (end-start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatSb(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("IterStrB: " + (end-start) + "ms");
}
public static String repeatLog2(String s, int times) {
if (times <= 0) {
return "";
}
else if (times % 2 == 0) {
return repeatLog2(s+s, times/2);
}
else {
return s + repeatLog2(s+s, times/2);
}
}
public static String repeatR(String s, int times) {
if (times <= 0) {
return "";
}
else {
return s + repeatR(s, times-1);
}
}
public static String repeatIc(String s, int times) {
String tmp = "";
for (int i = 0; i < times; i++) {
tmp += s;
}
return tmp;
}
public static String repeatSb(String s, int n) {
final StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; i++) {
sb.append(s);
}
return sb.toString();
}
}
它有两个参数,第一个是迭代的次数(每个函数运行的重复次数参数从1..n),第二个是要重复的字符串。
到目前为止,快速检查不同输入的运行时间,得出的排名大致如下(从好到坏):
迭代StringBuilder追加(1x)。
递归连接log2调用(~3x)。
递归级联线性调用(~30x)。
迭代级联线性(~45x)。
我从来没有想到递归函数比for循环快:-o
玩得开心。
基于fortran的答案,这是一个使用StringBuilder的递归版本:
public static void repeat(StringBuilder stringBuilder, String s, int times) {
if (times > 0) {
repeat(stringBuilder.append(s), s, times - 1);
}
}
public static String repeat(String s, int times) {
StringBuilder stringBuilder = new StringBuilder(s.length() * times);
repeat(stringBuilder, s, times);
return stringBuilder.toString();
}
如果你使用的是Java <= 7,这是最“简洁”的:
// create a string made up of n copies of string s
String.format("%0" + n + "d", 0).replace("0", s);
在Java 8及以上版本中,有一种更易读的方式:
// create a string made up of n copies of string s
String.join("", Collections.nCopies(n, s));
最后,对于Java 11及以上版本,有一个专门用于此目的的新的repeat (int count)方法(链接)
"abc".repeat(12);
或者,如果您的项目使用java库,则有更多的选项。
对于Apache Commons:
StringUtils.repeat("abc", 12);
谷歌番石榴:
Strings.repeat("abc", 12);
这是最新的Stringutils.java
public static String repeat(String str, int repeat) {
// Performance tuned for 2.0 (JDK1.4)
if (str == null) {
return null;
}
if (repeat <= 0) {
return EMPTY;
}
int inputLength = str.length();
if (repeat == 1 || inputLength == 0) {
return str;
}
if (inputLength == 1 && repeat <= PAD_LIMIT) {
return repeat(str.charAt(0), repeat);
}
int outputLength = inputLength * repeat;
switch (inputLength) {
case 1 :
return repeat(str.charAt(0), repeat);
case 2 :
char ch0 = str.charAt(0);
char ch1 = str.charAt(1);
char[] output2 = new char[outputLength];
for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
output2[i] = ch0;
output2[i + 1] = ch1;
}
return new String(output2);
default :
StringBuilder buf = new StringBuilder(outputLength);
for (int i = 0; i < repeat; i++) {
buf.append(str);
}
return buf.toString();
}
}
它甚至不需要这么大,可以做成这样,可以复制和粘贴
到项目中的实用程序类中。
public static String repeat(String str, int num) {
int len = num * str.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < times; i++) {
sb.append(str);
}
return sb.toString();
}
所以e5,我认为最好的方法是简单地使用上面提到的代码,或者这里的任何答案。但是如果它是一个小项目,Commons lang就太大了
我想要一个函数来为JDBC创建一个以逗号分隔的问号列表,然后找到了这篇文章。所以,我决定采用两个变体,看看哪个表现更好。在100万次迭代之后,普通的StringBuilder花了2秒(fun1),而神秘的最优版本(fun2)花了30秒。又变得神秘有什么意义?
private static String fun1(int size) {
StringBuilder sb = new StringBuilder(size * 2);
for (int i = 0; i < size; i++) {
sb.append(",?");
}
return sb.substring(1);
}
private static String fun2(int size) {
return new String(new char[size]).replaceAll("\0", ",?").substring(1);
}
我很喜欢这个问题。有很多知识和风格。所以我不能没有展示我的摇滚就离开;)
{
String string = repeat("1234567890", 4);
System.out.println(string);
System.out.println("=======");
repeatWithoutCopySample(string, 100000);
System.out.println(string);// This take time, try it without printing
System.out.println(string.length());
}
/**
* The core of the task.
*/
@SuppressWarnings("AssignmentToMethodParameter")
public static char[] repeat(char[] sample, int times) {
char[] r = new char[sample.length * times];
while (--times > -1) {
System.arraycopy(sample, 0, r, times * sample.length, sample.length);
}
return r;
}
/**
* Java classic style.
*/
public static String repeat(String sample, int times) {
return new String(repeat(sample.toCharArray(), times));
}
/**
* Java extreme memory style.
*/
@SuppressWarnings("UseSpecificCatch")
public static void repeatWithoutCopySample(String sample, int times) {
try {
Field valueStringField = String.class.getDeclaredField("value");
valueStringField.setAccessible(true);
valueStringField.set(sample, repeat((char[]) valueStringField.get(sample), times));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
你喜欢吗?
OOP的解决方案
几乎每个答案都提出了一个静态函数作为解决方案,但考虑到面向对象(为了可重用性和清晰度),我通过CharSequence-Interface(这也开放了可变charsequence -类的可用性)通过委托提出了一个解决方案。
下面的类可以使用或不使用Separator-String/CharSequence,并且每次调用“toString()”都会构建最终的重复String。
输入/分隔符不仅限于string类,而且可以是实现CharSequence的每个类(例如StringBuilder, StringBuffer等)!
源代码:
/**
* Helper-Class for Repeating Strings and other CharSequence-Implementations
* @author Maciej Schuttkowski
*/
public class RepeatingCharSequence implements CharSequence {
final int count;
CharSequence internalCharSeq = "";
CharSequence separator = "";
/**
* CONSTRUCTOR - RepeatingCharSequence
* @param input CharSequence to repeat
* @param count Repeat-Count
*/
public RepeatingCharSequence(CharSequence input, int count) {
if(count < 0)
throw new IllegalArgumentException("Can not repeat String \""+input+"\" less than 0 times! count="+count);
if(count > 0)
internalCharSeq = input;
this.count = count;
}
/**
* CONSTRUCTOR - Strings.RepeatingCharSequence
* @param input CharSequence to repeat
* @param count Repeat-Count
* @param separator Separator-Sequence to use
*/
public RepeatingCharSequence(CharSequence input, int count, CharSequence separator) {
this(input, count);
this.separator = separator;
}
@Override
public CharSequence subSequence(int start, int end) {
checkBounds(start);
checkBounds(end);
int subLen = end - start;
if (subLen < 0) {
throw new IndexOutOfBoundsException("Illegal subSequence-Length: "+subLen);
}
return (start == 0 && end == length()) ? this
: toString().substring(start, subLen);
}
@Override
public int length() {
//We return the total length of our CharSequences with the separator 1 time less than amount of repeats:
return count < 1 ? 0
: ( (internalCharSeq.length()*count) + (separator.length()*(count-1)));
}
@Override
public char charAt(int index) {
final int internalIndex = internalIndex(index);
//Delegate to Separator-CharSequence or Input-CharSequence depending on internal index:
if(internalIndex > internalCharSeq.length()-1) {
return separator.charAt(internalIndex-internalCharSeq.length());
}
return internalCharSeq.charAt(internalIndex);
}
@Override
public String toString() {
return count < 1 ? ""
: new StringBuilder(this).toString();
}
private void checkBounds(int index) {
if(index < 0 || index >= length())
throw new IndexOutOfBoundsException("Index out of Bounds: "+index);
}
private int internalIndex(int index) {
// We need to add 1 Separator-Length to total length before dividing,
// as we subtracted one Separator-Length in "length()"
return index % ((length()+separator.length())/count);
}
}
使用示例:
public static void main(String[] args) {
//String input = "12345";
//StringBuffer input = new StringBuffer("12345");
StringBuilder input = new StringBuilder("123");
//String separator = "<=>";
StringBuilder separator = new StringBuilder("<=");//.append('>');
int repeatCount = 2;
CharSequence repSeq = new RepeatingCharSequence(input, repeatCount, separator);
String repStr = repSeq.toString();
System.out.println("Repeat="+repeatCount+"\tSeparator="+separator+"\tInput="+input+"\tLength="+input.length());
System.out.println("CharSeq:\tLength="+repSeq.length()+"\tVal="+repSeq);
System.out.println("String :\tLength="+repStr.length()+"\tVal="+repStr);
//Here comes the Magic with a StringBuilder as Input, as you can append to the String-Builder
//and at the same Time your Repeating-Sequence's toString()-Method returns the updated String :)
input.append("ff");
System.out.println(repSeq);
//Same can be done with the Separator:
separator.append("===").append('>');
System.out.println(repSeq);
}
示例输出:
Repeat=2 Separator=<= Input=123 Length=3
CharSeq: Length=8 Val=123<=123
String : Length=8 Val=123<=123
123ff<=123ff
123ff<====>123ff
如果您关心的是速度,那么应该尽可能少地使用内存复制。因此,它需要处理字符数组。
public static String repeatString(String what, int howmany) {
char[] pattern = what.toCharArray();
char[] res = new char[howmany * pattern.length];
int length = pattern.length;
for (int i = 0; i < howmany; i++)
System.arraycopy(pattern, 0, res, i * length, length);
return new String(res);
}
为了测试速度,使用StirngBuilder的一个类似的优化方法如下:
public static String repeatStringSB(String what, int howmany) {
StringBuilder out = new StringBuilder(what.length() * howmany);
for (int i = 0; i < howmany; i++)
out.append(what);
return out.toString();
}
以及测试它的代码:
public static void main(String... args) {
String res;
long time;
for (int j = 0; j < 1000; j++) {
res = repeatString("123", 100000);
res = repeatStringSB("123", 100000);
}
time = System.nanoTime();
res = repeatString("123", 1000000);
time = System.nanoTime() - time;
System.out.println("elapsed repeatString: " + time);
time = System.nanoTime();
res = repeatStringSB("123", 1000000);
time = System.nanoTime() - time;
System.out.println("elapsed repeatStringSB: " + time);
}
下面是我的系统的运行结果:
elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937
注意,for循环测试是为了引入JIT并获得最佳结果。
字符串:重复
". ".repeat(7) // Seven period-with-space pairs: . . . . . . .
Java 11中的新方法是String::repeat,它完全符合你的要求:
String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");
它的Javadoc说:
/**
* Returns a string whose value is the concatenation of this
* string repeated {@code count} times.
* <p>
* If this string is empty or count is zero then the empty
* string is returned.
*
* @param count number of times to repeat
*
* @return A string composed of this string repeated
* {@code count} times or the empty string if this
* string is empty or count is zero
*
* @throws IllegalArgumentException if the {@code count} is
* negative.
*
* @since 11
*/
合并以供快速参考:
public class StringRepeat {
// Java 11 has built-in method - str.repeat(3);
// Apache - StringUtils.repeat(3);
// Google - Strings.repeat("",n);
// System.arraycopy
static String repeat_StringBuilderAppend(String str, int n) {
if (str == null || str.isEmpty())
return str;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append(str);
}
return sb.toString();
}
static String repeat_ArraysFill(String str, int n) {
String[] strs = new String[n];
Arrays.fill(strs, str);
return Arrays.toString(strs).replaceAll("\\[|\\]|,| ", "");
}
static String repeat_Recursion(String str, int n) {
if (n <= 0)
return "";
else
return str + repeat_Recursion(str, n - 1);
}
static String repeat_format1(String str, int n) {
return String.format(String.format("%%%ds", n), " ").replace(" ", str);
}
static String repeat_format2(String str, int n) {
return new String(new char[n]).replace("\0", str);
}
static String repeat_format3(String str, int n) {
return String.format("%0" + n + "d", 0).replace("0", str);
}
static String repeat_join(String str, int n) {
return String.join("", Collections.nCopies(n, str));
}
static String repeat_stream(String str, int n) {
return Stream.generate(() -> str).limit(n).collect(Collectors.joining());
}
public static void main(String[] args) {
System.out.println(repeat_StringBuilderAppend("Mani", 3));
System.out.println(repeat_ArraysFill("Mani", 3));
System.out.println(repeat_Recursion("Mani", 3));
System.out.println(repeat_format1("Mani", 3));
System.out.println(repeat_format2("Mani", 3));
System.out.println(repeat_format3("Mani", 3));
System.out.println(repeat_join("Mani", 3));
System.out.println(repeat_stream("Mani", 3));
}
}
如果你只知道输出字符串的长度(它可能不能被输入字符串的长度整除),那么使用这个方法:
static String repeat(String s, int length) {
return s.length() >= length ? s.substring(0, length) : repeat(s + s, length);
}
使用演示:
for (int i = 0; i < 50; i++)
System.out.println(repeat("_/‾\\", i));
不要使用空s,长度为> 0,因为在这种情况下不可能得到想要的结果。