This is an issue when compilation or preparation methods are called inside loop bodies with constant or loop-invariant arguments. This includes pattern compilation methods for regular expressions, string methods that accept regular expression patterns (such as match, replace, and split operations), and database statement preparation methods.
In Java, this specifically refers to Pattern.compile(), String regex methods (matches(), replaceAll(),
replaceFirst(), split()), and Connection.prepareStatement().
Compilation and preparation operations are expensive because they involve parsing, validation, and internal representation building. When these operations are performed inside loops with constant or loop-invariant arguments, the same work is repeated unnecessarily on every iteration.
When you call functions that compile regular expressions from strings or use string methods that accept regex patterns, the language runtime must:
These steps happen every time, even when the pattern string is identical. For example, calling a string matching method with a pattern like
"\d+" inside a loop that processes 1,000 items means compiling the same pattern 1,000 times.
When you call methods that create prepared statements from SQL strings, the database driver must:
Prepared statements exist specifically to avoid this overhead by allowing you to compile once and execute many times with different parameters. Calling statement preparation methods inside a loop with the same SQL string defeats this purpose entirely.
The repeated compilation/preparation causes:
The application may experience:
String.split() does not compile a regular expression when the argument meets either of these conditions:
In these cases, no issue is raised.
For regular expression operations, compile the Pattern once before the loop and reuse it inside the loop. Do not move
String.matches(), replaceAll(), replaceFirst(), or split() outside the loop: the receiver still
changes on each iteration. Replace those calls with Pattern.matcher() or Pattern.split() applied to each input.
For database operations, prepare the statement once before the loop and reuse it with different parameters.
for (String input : inputs) {
Pattern p = Pattern.compile("[a-z]+"); // Noncompliant
Matcher m = p.matcher(input);
if (m.find()) {
handle(m.group());
}
}
Pattern LOWER = Pattern.compile("[a-z]+");
for (String input : inputs) {
Matcher m = LOWER.matcher(input);
if (m.find()) {
handle(m.group());
}
}
for (String input : inputs) {
if (input.matches("[a-z]+")) { // Noncompliant
handle(input);
}
}
Pattern LOWER = Pattern.compile("[a-z]+");
for (String input : inputs) {
if (LOWER.matcher(input).matches()) {
handle(input);
}
}
for (int id : ids) {
PreparedStatement ps = connection.prepareStatement("SELECT * FROM t WHERE id = ?"); // Noncompliant
ps.setInt(1, id);
ps.execute();
ps.close();
}
PreparedStatement ps = connection.prepareStatement("SELECT * FROM t WHERE id = ?");
for (int id : ids) {
ps.setInt(1, id);
ps.execute();
}