Skip to content

Commit d8c0025

Browse files
committed
Use memoization/caching in applicable StringOperations methods
1 parent 292551e commit d8c0025

File tree

1 file changed

+94
-22
lines changed

1 file changed

+94
-22
lines changed

src/main/java/core/StringOperations.java

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,63 @@
11
package core;
22

33
import java.util.ArrayList;
4+
import java.util.HashMap;
45
import java.util.List;
6+
import java.util.Map;
57

68
public class StringOperations {
79

810
/**
911
* Determines whether a given position in a line of text is currently inside a string literal, accounting for
1012
* escaped quotes.
1113
*
12-
* <p>This method scans the input line from the beginning up to (but not including) the specified position.
13-
* It toggles the {@code inString} flag each time it encounters a non-escaped double quote character.
14-
* This helps determine whether the character at the given position falls within a string.</p>
14+
* <p>
15+
* When called, this method checks if the "in-string" state for the provided line has already been computed and cached.
16+
* If so, it returns the cached result for the given position in O(1) time. If not, it computes the in-string state for
17+
* every character in the line, caches the result, and then returns the value for the requested position.
18+
* </p>
1519
*
1620
* @param line
1721
* The input string to analyze.
1822
* @param position
1923
* The index position in the string to check.
2024
*
2125
* @return {@code true} if the position is within a string literal, {@code false} otherwise.
26+
*
27+
* @see #clearInStringCache()
2228
*/
2329
public static boolean isInString(String line, int position) {
30+
if (inStringCache == null) inStringCache = new HashMap<String, Boolean[]>();
31+
32+
// If the String is already in the cache, return the inString value for the given position.
33+
Boolean[] inStringArr = inStringCache.get(line);
34+
if (inStringArr != null) {
35+
return inStringArr[position];
36+
}
37+
38+
// Otherwise, precompute the cache entry for this String and call this method again.
39+
inStringArr = new Boolean[line.length()];
2440
boolean inString = false;
25-
for (int i = 0; i <= position; i++) { // Changed <= to < to exclude current position
26-
if (i == position && line.charAt(i) == '"' && (i == 0 || line.charAt(i - 1) != '\\')) {
27-
return false;
28-
}
41+
for (int i = 0; i < line.length(); i++) {
2942
if (line.charAt(i) == '"' && (i == 0 || line.charAt(i - 1) != '\\')) {
3043
inString = !inString;
3144
}
45+
inStringArr[i] = inString;
3246
}
33-
return inString;
47+
inStringCache.put(line, inStringArr);
48+
return isInString(line, position);
3449
}
50+
private static Map<String, Boolean[]> inStringCache;
51+
public static void clearInStringCache() { inStringCache = null; }
3552

3653
/**
37-
* Determines whether the specified position in a line of text is currently inside a JSON array.
38-
*
39-
* <p>This method traverses the line up to (but not including) the given position, tracking array nesting depth.
40-
* It increments depth when encountering a non-escaped '[' character outside of a string, and decrements it for ']'.
41-
* If the resulting depth is greater than 0 at the given position, the position is considered to be inside an array.</p>
54+
* Determines whether the specified position in a line of text is currently inside a JSON array (which is marked by square brackets {@code []}).
55+
* <p>
56+
* This method uses caching to improve performance for repeated queries on the same string. When called, it checks if the
57+
* "in-array" state for the provided line has already been computed and cached. If so, it returns the cached result for
58+
* the given position in O(1) time. If not, it computes the in-array state for every character in the line, caches the
59+
* result, and then returns the value for the requested position.
60+
* </p>
4261
*
4362
* @param line
4463
* The input string to analyze.
@@ -48,28 +67,81 @@ public static boolean isInString(String line, int position) {
4867
* @return {@code true} if the position is within a JSON array, {@code false} otherwise.
4968
*
5069
* @see #isInString(String, int)
70+
* @see #clearInArrayCache()
5171
*/
5272
public static boolean isInArray(String line, int position) {
73+
if (inArrayCache == null) inArrayCache = new HashMap<>();
74+
75+
// If the String is already in the cache, return the inArray value for the given position.
76+
Boolean[] inArrayArr = inArrayCache.get(line);
77+
if (inArrayArr != null) {
78+
return inArrayArr[position];
79+
}
80+
81+
// Otherwise, precompute the cache entry for this String and call this method again.
82+
inArrayArr = new Boolean[line.length()];
5383
int depth = 0;
54-
for (int i = 0; i < position; i++) {
55-
if (line.charAt(i) == '[' && !isInString(line, i - 1))
84+
for (int i = 0; i < line.length(); i++) {
85+
if (line.charAt(i) == '[' && (i == 0 || !isInString(line, i - 1)))
5686
depth++;
57-
else if (line.charAt(i) == ']' && !isInString(line, i - 1))
87+
else if (line.charAt(i) == ']' && (i == 0 || !isInString(line, i - 1)))
5888
depth--;
89+
inArrayArr[i] = depth > 0;
5990
}
60-
return depth == 0 ? false : true;
91+
inArrayCache.put(line, inArrayArr);
92+
return isInArray(line, position);
6193
}
94+
private static Map<String, Boolean[]> inArrayCache;
95+
public static void clearInArrayCache() { inStringCache = null; }
6296

97+
98+
/**
99+
* Determines whether the specified position in a line of text is currently inside a JSON object (which is marked by curly braces {@code &#123;&#125;}).
100+
* <p>
101+
* This method uses caching to improve performance for repeated queries on the same string. When called, it checks if the
102+
* "in-object" state for the provided line has already been computed and cached. If so, it returns the cached result for
103+
* the given position in O(1) time. If not, it computes the in-object state for every character in the line, caches the
104+
* result, and then returns the value for the requested position.
105+
* </p>
106+
* <p>
107+
* This caching approach greatly improves performance when the same string is analyzed multiple times, especially for
108+
* large strings or when called in a loop.
109+
* </p>
110+
*
111+
* @param line
112+
* The input string to analyze.
113+
* @param position
114+
* The index position in the string to check.
115+
*
116+
* @return {@code true} if the position is within a JSON object, {@code false} otherwise.
117+
*
118+
* @see #isInString(String, int)
119+
* @see #clearInObjectCache()
120+
*/
63121
public static boolean isInObject(String line, int position) {
122+
if (inObjectCache == null) inObjectCache = new HashMap<>();
123+
124+
// If the String is already in the cache, return the inObject value for the given position.
125+
Boolean[] inObjectArr = inObjectCache.get(line);
126+
if (inObjectArr != null) {
127+
return inObjectArr[position];
128+
}
129+
130+
// Otherwise, precompute the cache entry for this String and call this method again.
131+
inObjectArr = new Boolean[line.length()];
64132
int depth = 0;
65-
for (int i = 0; i < position; i++) {
66-
if (line.charAt(i) == '{' && !isInString(line, i))
133+
for (int i = 0; i < line.length(); i++) {
134+
if (line.charAt(i) == '{' && (i == 0 || !isInString(line, i)))
67135
depth++;
68-
else if (line.charAt(i) == '}' && !isInString(line, i))
136+
else if (line.charAt(i) == '}' && (i == 0 || !isInString(line, i)))
69137
depth--;
138+
inObjectArr[i] = depth > 0;
70139
}
71-
return depth == 0 ? false : true;
140+
inObjectCache.put(line, inObjectArr);
141+
return isInObject(line, position);
72142
}
143+
private static Map<String, Boolean[]> inObjectCache;
144+
public static void clearInObjectCache() { inObjectCache = null; }
73145

74146
/**
75147
* Checks if the given line contains at least one unquoted occurrence of the target character.
@@ -90,7 +162,7 @@ else if (line.charAt(i) == '}' && !isInString(line, i))
90162
* @see #isInString(String, int)
91163
*/
92164
public static boolean containsUnquotedChar(String line, char target) {
93-
return countUnquotedChar(line, target) > 0;
165+
return findFirstUnquotedChar(line, target) != -1;
94166
}
95167

96168
/**

0 commit comments

Comments
 (0)