“静态”模式不应该总是静态的吗?

Posted

技术标签:

【中文标题】“静态”模式不应该总是静态的吗?【英文标题】:Shouldn't "static" patterns always be static? 【发布时间】:2011-06-23 13:00:09 【问题描述】:

我刚刚在一些我没有写的代码中发现了一个错误,我有点惊讶:

Pattern pattern = Pattern.compile("\\d1,2.\\d1,2.\\d4");
Matcher matcher = pattern.matcher(s);

尽管这段代码在我们得到的输入数据上失败了 (因为它试图以 17.01.2011 格式查找日期并取回 10396/2011 之类的内容,然后由于无法解析日期,但 真的 不是这个问题的重点;) 我想知道:

Pattern.compile 的重点之一不是作为速度优化(通过预编译正则表达式)吗?

所有“静态”模式不应该总是编译成静态模式吗?

网络上有很多示例,其中总是使用 Pattern.compile 重新编译相同的模式,我开始怀疑我是否看到了东西。

不是(假设字符串是静态的,因此不是动态构造的):

static Pattern pattern = Pattern.compile("\\d1,2.\\d1,2.\\d4");

总是优于非静态模式引用?

【问题讨论】:

模式中的错误是. 匹配任何东西。使用\.(或者更确切地说\\.;第一个反斜杠用于Java)来解决这个问题。 @Donal Fellows:非常感谢,我知道我知道,我只是想在阅读时粘贴损坏的代码。对我来说,这段代码中有 两个 WTF:首先,模式编译不是静态的,其次,它是一种严重的 regexp-now-you-have-two-problems 问题:) 所有说静态编译更好的答案都是正确的。但是这里有一点过早的优化。如果您在网络上看到许多使用 Pattern.compile 非静态方式的示例,那可能是因为它通常不是瓶颈,并且可能只是更容易阅读或维护。始终在优化之前进行测量,否则您可能会发现仅仅探索问题所花费的时间比您的程序在 Pattern.compile 中花费的所有 CPU 时间加起来还要多:-)。 @Avi:阅读起来并不容易。当它是静态的时,你可以给你的模式一个有意义的名字。此外,谈到 “过早的优化” 只是好的 Java 实践也很奇怪。阅读此线程的人编写的所有程序所赢得的时间使问题和正确答案值得。 这就是为什么我将它作为评论而不是答案提供的原因。 :-) 【参考方案1】:
    是的,预编译 Pattern 的全部意义在于只执行一次。 这实际上取决于您将如何使用它,但一般来说,存储在static 字段中的预编译模式应该没问题。 (与 Matchers 不同,它不是线程安全的,因此根本不应该存储在字段中,无论是否静态。)

在静态初始化程序中编译模式的唯一警告是,如果模式无法编译并且静态初始化程序抛出异常,那么追踪错误的来源可能会非常烦人。这是一个小的可维护性问题,但可能值得一提。

【讨论】:

使用一个好的 IDE 肯定会有所帮助... IntelliJ IDEA 会清楚地指出模式中无法编译的错误(即使在不完整的源代码上)。 @SyntaxT3rr0r 这是一个很酷的功能。 (顺便说一句,我并没有忘记你的代理-GC 问题,我刚刚意识到我忘记了如何用 C 编写代码,所以想出一个可行的解决方案需要更长的时间。)【参考方案2】:

首先,pattern 中的错误是因为点 (.) 匹配所有内容。如果要匹配点 (.),则必须在正则表达式中对其进行转义:

Pattern pattern = Pattern.compile("\\d1,2\\.\\d1,2\\.\\d4");

其次,Pattern.compile() 是重法。始终建议仅初始化一次静态模式(我的意思是未更改或未即时生成的模式)。实现此目的的流行方法之一是将Pattern.compile() 放入静态初始化程序中。

您可以使用其他方法。例如使用单例模式或使用创建单例对象的框架(如 Spring)。

【讨论】:

我知道这是因为点匹配所有内容;)我想在这种情况下我会使用静态初始化器:使用 Spring 的单例模式来创建 Pattern 的一个实例似乎有点极端:) 当然,我不建议你只使用 Spring 来创建模式的实例。刚才说了,除了静态初始化,还有其他解决方案。我的意思是,如果您已经在项目中使用了 spring,您可以将所有模式放到一个单例 bean 中,并在需要时检索它们。 @AlexR 在static 等静态初始化程序中实例化Pattern 与将Pattern 声明为private static final Pattern pattern = Pattern.compile() 等静态字段有何不同? @KevinBowersox,我认为如果您使用 litteral 字符串模式定义 + static + final,它们是相同的,除了加载类时它们的评估顺序。 @ChristopheRoussy,非静态字段在创建类实例时被初始化。因此,如果Pattern 类型的成员不是静态的,则为每个对象调用其compile() 方法。但是,如果成员是静态的,则此方法仅在第一次使用类时调用一次。【参考方案3】:

是的,每次使用时都编译 Pattern 很浪费,静态定义它会带来更好的性能。请参阅this SO thread 进行类似讨论。

【讨论】:

【参考方案4】:

只要加载类,静态模式就会保留在内存中。

如果您担心内存问题并想要一个不时使用的一次性Pattern,并且在您使用完它后会收集垃圾,那么您可以使用非静态Pattern

【讨论】:

【参考方案5】:

这是一个经典的时间与内存权衡。 如果您只编译一次模式,请不要将其粘贴在静态字段中。 如果您测量到编译模式很慢,请预编译并将其放在静态字段中。

【讨论】:

以上是关于“静态”模式不应该总是静态的吗?的主要内容,如果未能解决你的问题,请参考以下文章

*可以*为静态的C#方法应该是静态的吗? [关闭]

Java静态代理和iOS代理模式这两个概念的理解上的疑惑

RestTemplate 应该是静态全局声明的吗?

单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]

单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]

用于提供静态 HTML 文件的 Django 设计模式