【问题标题】:What is the fastest way to find the last part of a url path?找到 url 路径最后一部分的最快方法是什么?
【发布时间】:2016-01-16 16:06:11
【问题描述】:

我有一个网址,例如:

“http://www.someco.com/news/2016-01-03/waterloo-station”

网址从不包含查询字符串。

提取字符串“滑铁卢站”的最干净的方法是什么?

当然可以使用下面的代码:

url.substring(url.lastIndexOf('/') + 1))

但我对它并不完全满意,因为它必须执行对最后一个索引的搜索,然后获取子字符串。我想知道是否有更好的方法(使用正则表达式?)在一个步骤中获得相同的结果。

当然,当执行数十亿次时,解决方案应该会明显更快。

【问题讨论】:

  • 我正在为此进行大规模的过早优化。
  • 您为什么认为这可以更快?一个人必须找到分隔符,一个人必须构造一个新字符串。没有办法解决这个问题。
  • 正则表达式比仅迭代 char 数组的 17 个元素要复杂得多。这是尽可能快的,并且也是尽可能简单和可读的。它成为应用程序性能问题的原因的可能性为 0:如果您必须执行数十亿次,则需要以某种方式从磁盘读取这些 URL,这比子字符串慢许多数量级。跨度>
  • 我不需要阅读它们数十亿次,因为我没有数十亿个网址可供阅读。我有数百万个,我不想浪费内存来保存子字符串的结果。我需要多次执行子字符串,因为算法需要它。
  • @JohnHenry - 即使存在这样的方法,它也不会比上面的代码快。

标签: java regex


【解决方案1】:

我不认为它可以改进。简短的回答是,因为搜索最后一个索引是一个简单的操作,所以可以使用快速算法(直接在 String 类中!)来实现,而正则表达式很难做到这么快。 如您所见,第二次访问 String 的成本不能少:它只是新 String 的初始化。

如果有直接在 String 类中实现的专用方法,可能会更快。

如果您想了解更多细节,您可以自己查看 JDK 中的代码。为了您的方便,复制到这里。

以下代码是我的JDK中lastIndexOf()方法的实现:

public int lastIndexOf(int ch, int fromIndex) {
    int min = offset;
    char v[] = value;

    int i = offset + ((fromIndex >= count) ? count - 1 : fromIndex);

    if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
        // handle most cases here (ch is a BMP code point or a
        // negative value (invalid code point))
        for (; i >= min ; i--) {
            if (v[i] == ch) {
                return i - offset;
            }
        }
        return -1;
    }

    int max = offset + count;
    if (ch <= Character.MAX_CODE_POINT) {
        // handle supplementary characters here
        char[] surrogates = Character.toChars(ch);
        for (; i >= min; i--) {
            if (v[i] == surrogates[0]) {
                if (i + 1 == max) {
                    break;
                }
                if (v[i+1] == surrogates[1]) {
                    return i - offset;
                }
            }
        }
    }
    return -1;
}

直接在 String 类中实现,它可以访问其私有成员:

/** The value is used for character storage. */
private final char value[];

/** The offset is the first index of the storage that is used. */
private final int offset;

/** The count is the number of characters in the String. */
private final int count;

它不适用于子字符串。 同时,Java 中的 substring 方法非常快,因为它不会创建一个新的 char 数组,而是简单地创建一个新的 String 对象,改变偏移量和计数:

public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > count) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    if (beginIndex > endIndex) {
        throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
    }
    return ((beginIndex == 0) && (endIndex == count)) ? this :
        new String(offset + beginIndex, endIndex - beginIndex, value);
}

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

【讨论】:

    【解决方案2】:

    String.valueOf(Paths.get(file).getFileName())

    【讨论】:

    • 不幸的是,这不起作用,因为“InvalidPathException: Illegal char <:> at index 4”
    【解决方案3】:

    不确定这是否是获取“文件名”的最快方法,但它非常简单快捷:

    var url = "http://www.someco.com/news/2016-01-03/waterloo-station";
    var fileName = Path.of(new URI(url).getPath()).getFileName();
    

    【讨论】:

      猜你喜欢
      • 2012-12-18
      • 1970-01-01
      • 2023-01-04
      • 2011-05-24
      • 2014-05-22
      • 2019-04-15
      • 2020-02-20
      • 1970-01-01
      • 2012-12-29
      相关资源
      最近更新 更多