Java 8-如何仅通过一个遍历就从初始字符串构建一个字符串

时间:2018-11-14 14:10:37

标签: java java-8 java-stream

我有一个网址,例如:String url = "https://.../foo/a/555/data1";

目标:将网址转换为字符串:a555data1

我只希望构建一次遍历字符串一次的结果。 我决定进行以下过程:

  1. 我想从背面开始“串流”字符串。
  2. 如果不是反斜杠,请在双端队列的前面插入/附加。
  3. 如果是第三个反斜杠结尾

我已经在下面成功编写了一个糟糕的解决方案,可以使用流使其变得漂亮吗?

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");

6 个答案:

答案 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-无论如何将在连接过程中使用它。