将java String与给定字符序列交错的最佳方法是什么。交错间隔应该是可以改变的。
示例:
String s = " .... 0000000000000 ..."; // length random
String b = interleave(s, 3, "-");
结果:
... 000-000-000-000-000 ...
另一个例子:
String s = " .... we all we all we all ...";
String b = interleave(s, 7, "rock ");
结果:
... we all rock we all rock we all rock ...
如果字符串长度不是交错距离的倍数,该函数也应该有效。 有什么建议?是否(再次)采用'公地'的方式来做这件事?
答案 0 :(得分:14)
这是非常简单且相当可读的实现(我在下面的基准测试中将其称为 StringBuilder ):
public static String interleave(String s, int interval, String separator)
{
StringBuilder sb = new StringBuilder(s);
for (int pos = (s.length()-1) / interval; pos > 0; pos--)
{
sb.insert(pos * interval, separator);
}
return sb.toString();
}
如果您关注简单的 StringBuilder 实现的效率,那么这个实现可能会更好地满足您的需求(我在下面的基准测试中将其称为 Arrays ):
public static String interleave(String string, int interval, String separator)
{
char[] src = string.toCharArray();
char[] sep = separator.toCharArray();
int count = (src.length-1)/interval;
char[] dst = new char[src.length + count * sep.length];
int srcpos = 0, dstpos = 0;
for (int i = 0; i < count; i++)
{
System.arraycopy(src, srcpos, dst, dstpos, interval);
srcpos += interval;
dstpos += interval;
System.arraycopy(sep, 0, dst, dstpos, sep.length);
dstpos += sep.length;
}
if (dstpos < dst.length)
{
System.arraycopy(src, srcpos, dst, dstpos, dst.length - dstpos);
}
return String.valueOf(dst);
}
注意:我可能只在J2ME环境下使用这种实现,但在巨大的字符串上它应该更快。虽然可读性很差......
当然,总是有一种RegExp做事的方式,在你爬过编译RegExp本身就不再存在问题的长度之后会出乎意料地快得多(你不能预编译一个RegExp,因为它是在飞行取决于间隔,感谢Rubens Farias指出这一点,不知何故错过了我自己)。所以这里(我在下面的基准测试中称之为 RegExp ):
public static String interleave(String string, int interval, String separator)
{
return string.replaceAll("(.{"+interval+"})", "$1"+Matcher.quoteReplacement(separator));
}
注意:如果字符串的长度是间隔的倍数,则此实现在末尾插入分隔符(而其他实现则不是)。我不喜欢RegExps,因为它们不可读也不太快。哦,你可以很容易地忘记“quoteReplacement”部分,如果分隔符包含“$ 1”甚至更糟 - 如果它来自用户,那么你就会遇到大麻烦。
此时我做了一些基准测试,所以第一个实现字符串长度100000需要0.002643秒,第二个实现 - 0.000010,第三个 - 0.000071,但一切都取决于字符串长度。
Length StringBuilder Arrays RegExp
10000 0.000012 0.000001 0.000054
100000 0.002643 0.000010 0.000071
1000000 0.315413 0.000026 0.000199
这绝不是一个严肃的基准测试,但它仍然显示了所涉算法的趋势和复杂性。
注意:尽管使用这些想法很有趣,但在使用大小小于1M的字符串时,我们仍然在谈论亚秒级改进 。因此,如果你只处理大小达1K的字符串(它将是0ms对0ms),那么你走哪条路并不重要。最重要的是它应该是可读的,直截了当的,并且不需要花太多时间来编写,因为我确信你有更多重要的问题需要解决,除非你正在编写一个通用库供所有人在最奇怪的情况下使用。记住 - 你的时间比CPU时间更有价值。
我将采用 Arrays 实现,因为这似乎最容易改变:
public static String interleave(String string, int interval, String separator, boolean fromRight)
{
char[] src = string.toCharArray();
char[] sep = separator.toCharArray();
int count = (src.length-1)/interval;
char[] dst = new char[src.length + count * sep.length];
int srcpos = 0, dstpos = 0;
if (fromRight)
{
srcpos = dstpos = src.length - count * interval;
if (srcpos > 0) System.arraycopy(src, 0, dst, 0, srcpos);
if (count > 0)
{
System.arraycopy(sep, 0, dst, dstpos, sep.length);
dstpos += sep.length;
count--;
}
}
for (int i = 0; i < count; i++)
{
System.arraycopy(src, srcpos, dst, dstpos, interval);
srcpos += interval;
dstpos += interval;
System.arraycopy(sep, 0, dst, dstpos, sep.length);
dstpos += sep.length;
}
if (dstpos < dst.length)
{
System.arraycopy(src, srcpos, dst, dstpos, dst.length - dstpos);
}
return String.valueOf(dst);
}
答案 1 :(得分:1)
我认为这个解决方案非常有效。不涉及数组副本或StringBuilder
扩展:
public static String interleave(String input, int interval, String sep) {
StringBuilder sb = new StringBuilder(input.length() + (((input.length() -1) / interval) * sep.length()));
char[] array = input.toCharArray();
for (int i = 0; i < array.length; i += interval) {
int span = i + interval;
for (int j = i; j < Math.min(span, array.length); j++) {
sb.append(array[j]);
}
if (span < array.length)
sb.append(sep);
}
return sb.toString();
}
答案 2 :(得分:1)
这是C#,但我确信Java有类似的方法:
public static string interleave(string input, int interval, string separator)
{
if (String.IsNullOrEmpty(input) ||
String.IsNullOrEmpty(separator) || interval <= 0)
return input;
int length = input.Length + // original length + added chars - last occur
((input.Length / interval) * separator.Length) -
(input.Length % interval == 0 ? separator.Length : 0);
return Regex.Replace( // magic happens here
input, String.Format("(.{{{0}}})", interval),
"$1" + separator.Replace("$", "$$")).Substring(0, length);
}
答案 3 :(得分:0)
这是有效和明确的:
public static String interleave(String s, int interval, String separator) {
StringBuffer b = new StringBuffer();
int length = s.length();
for (int start = 0; start < length - 1; start += interval) {
int end = Math.min(length, start + interval);
b.append(s.substring(start, end));
b.append(separator);
}
if (length % interval > 0) {
b.append(s.substring(length - (length % interval)));
}
return b.toString();
}
答案 4 :(得分:0)
使用the prerelease Google Guava libraries:
Joiner.on("-").join(Splitter.fixedLength(3).split(inputString));
简短,清晰,富有表现力。喜欢它!