我有一个网址,例如:String url = "https://.../foo/a/555/data1";
目标:将网址转换为字符串:a555data1
我只希望构建一次遍历字符串一次的结果。 我决定进行以下过程:
我已经在下面成功编写了一个糟糕的解决方案,可以使用流使其变得漂亮吗?
Deque<String> lifo = new ArrayDeque<>();
int count = 0;
for (int i = testUrl.length() - 1; count < 3 ; --i) {
if (testUrl.codePointAt(i) == ((int) '/') ) {
++count;
continue;
}
result.addFirst(testUrl.substring(i,i+1));
}
String foo = result.stream().collect(Collectors.joining());
assertThat(foo).isEqualTo("a606KAM1");
答案 0 :(得分:4)
另一种方法是正则表达式:
String result = url.replaceAll(".+/(.+)/(.+)/(.+)", "$1$2$3");
答案 1 :(得分:3)
您可以这样做
final String[] splitStrArr = url.split("/");
String result = Arrays.stream(splitStrArr).skip(splitStrArr.length - 3)
.collect(Collectors.joining(""));
答案 2 :(得分:3)
一种没有循环和流的替代解决方案:
String[] split = url.split("/");
int n = split.length;
return split[n - 3] + split[n - 2] + split[n - 1];
答案 3 :(得分:3)
如果您想真正快速地做到这一点,就必须减少每次字符串构造时发生的数据复制量。
lambda: a
即使在扩展它以支持可配置数量的零件时,
int ix1 = url.lastIndexOf('/'), ix2 = url.lastIndexOf('/', ix1-1),
ix3 = url.lastIndexOf('/', ix2-1);
String result = new StringBuilder(url.length() - ix3 - 3)
.append(url, ix3+1, ix2)
.append(url, ix2+1, ix1)
.append(url, ix1+1, url.length())
.toString();
它可能比所有替代品都快。
答案 4 :(得分:1)
最初,我的想法是,由于假定的性能开销,不应在此处使用流,因此我为另一个答案中提出的解决方案创建了一些性能测试:
import static java.util.Arrays.stream;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1)
public class CJMH {
@State(Scope.Thread)
public static class CState {
public String url = "https://.../foo/a/555/data1";
}
@Benchmark
public String fastest(CState state) {
String url = state.url;
int chunks = 3;
int[] ix = new int[chunks];
int index = url.length();
for(int a = ix.length-1; a >= 0; a--) index = url.lastIndexOf('/', (ix[a] = index)-1);
StringBuilder sb = new StringBuilder(url.length() - index - chunks);
for(int next: ix) sb.append(url, index+1, index = next);
return sb.toString();
}
@Benchmark
public String splitAndStreams(CState state) {
final String[] splitStrArr = state.url.split("/");
String result = stream(splitStrArr).
skip(splitStrArr.length - 3).
collect(Collectors.joining(""));
return result;
};
@Benchmark
public String splitAndIterate(CState state) {
final String[] splitStrArr = state.url.split("/");
String result = "";
for (int k=splitStrArr.length - 3; k<splitStrArr.length; k++) {
result += splitStrArr[k];
}
return result;
};
@Benchmark
public String splitAndSum(CState state) {
String[] split = state.url.split("/");
int n = split.length;
return split[n - 3] + split[n - 2] + split[n - 1];
};
@Benchmark
public String regexp(CState state) {
return state.url.replaceAll(".+/(.+)/(.+)/(.+)", "$1$2$3");
};
}
输出为:
Benchmark Mode Cnt Score Error Units
CJMH.fastest avgt 5 46.731 ± 0.445 ns/op
CJMH.regexp avgt 5 937.797 ± 11.928 ns/op
CJMH.splitAndIterate avgt 5 194.626 ± 1.880 ns/op
CJMH.splitAndStreams avgt 5 275.640 ± 1.887 ns/op
CJMH.splitAndSum avgt 5 180.257 ± 2.986 ns/op
因此,令人惊讶的是,流绝没有比遍历数组慢得多。最快的是@Holger在this answer中提供的无复制算法。而且,如果可以避免,请不要使用正则表达式!
答案 5 :(得分:0)
我可能会将您的代码简化为:
# A tibble: 5 x 4
day_of_week Bob Jack Simon
<chr> <fct> <fct> <fct>
1 Friday NA NA 0.1693
2 Monday NA 0.2346 NA
3 Thursday NA NA 0.7356
4 Tuesday 0.7635 NA NA
5 Wednesday 0.7253 NA NA
我认为在此处使用流没有真正意义-URL的长度不足以证明建立流所花费的时间是合理的。我们也可以使用StringBuilder-无论如何将在连接过程中使用它。