###Questions
Questions
Potential slowness causes
###Potential slowness causes
II guess your String.split
could be the culprit (with more than one character a regex gets created; you should use
###Review
Review
Summary
###Summary OverallOverall, it's not bad. When speed is important, I'd go for something like
###Filling the maps
Filling the maps
###Questions
###Potential slowness causes
I guess your String.split
could be the culprit (with more than one character a regex gets created; you should use
###Review
###Summary Overall, it's not bad. When speed is important, I'd go for something like
###Filling the maps
Questions
Potential slowness causes
I guess your String.split
could be the culprit (with more than one character a regex gets created; you should use
Review
Summary
Overall, it's not bad. When speed is important, I'd go for something like
Filling the maps
MyParser parser = new MyParser(response).skip("hasProcess=");
boolean hasProcess = parser.readBoolean();
parser.skip("\nversion=");
int version = parser.readInt();
while (parser.skipIfLookingAt("\nDATACENTER=")) {
parser.parseDatacenter(parser, whatever...);
}
where content
and index
are instance variables. This part doesn't need any substring creation at all (and gets a bit unreadable because of this). Warning: Doing something like content = content.substring(index)
(so you could use startsWith
) would make it clearer but terribly slow as you'd copy a big part of the string a lot of times.
###Filling the maps
Now, I'd probably move all the functionality into MyParser
, so that the parsing would be just
private void parseResponse(String response) {
new MyParser(response).parse();
}
The parser would have fields like
private final Map<String, Map<Integer, String>> primaryData;
so that I could write
void parseDatacenter() {
String datacenter = readTill('\n');
skip("\n\tTotalNumberOfServers:");
int totalNumberOfServers = readInt();
skip("\n\tprimary{");
primaryData.put(datacenter, readMap());
skip("\n\t");
}
Map<String, String> readMap() {
Map<String, String> result = new HashMap<>();
while (true) {
String key = readTillAndSkip('=');
String value = readTillOneOf("},");
map.put(key, value);
if (lookingAt('}') {
break;
}
skip(", ");
}
return result;
}
As an example see this simple method
String readTillOneOf(String terminators) {
int start = index;
while (terminators.indexOf(content.charAt(index)) > -1) {
++index;
}
return content.substring(start, index);
}
I ignored quite some details like parsing numbers and I can't tell how many parsing methods you'll need, but basically it's all pretty simple. Just skip what's fixed and process what you want while looking for a terminator.
It could get a bit more complicated if you needed to be error-tolerant, like allowing multiple spaces, but then you could let some of your methods skip over them. Or, if it gets difficult, use regexes. But I don't think it gets complicated.
Note that I'm unsure why your parser is slow. There's a lot of splitting and this is the probable cause, but it's just a guess.
MyParser parser = new MyParser(response).skip("hasProcess=");
boolean hasProcess = parser.readBoolean();
parser.skip("\nversion=");
int version = parser.readInt();
while (parser.skipIfLookingAt("\nDATACENTER=")) {
parseDatacenter(parser, whatever...);
}
where content
and index
are instance variables. This part doesn't need any substring creation at all (and gets a bit unreadable because of this). Warning: Doing something like content = content.substring(index)
(so you could use startsWith
) would make it clearer but terribly slow as you'd copy a big part of the string a lot of times.
MyParser parser = new MyParser(response).skip("hasProcess=");
boolean hasProcess = parser.readBoolean();
parser.skip("\nversion=");
int version = parser.readInt();
while (parser.skipIfLookingAt("\nDATACENTER=")) {
parser.parseDatacenter(parser, whatever...);
}
where content
and index
are instance variables. This part doesn't need any substring creation at all (and gets a bit unreadable because of this). Warning: Doing something like content = content.substring(index)
(so you could use startsWith
) would make it clearer but terribly slow as you'd copy a big part of the string a lot of times.
###Filling the maps
Now, I'd probably move all the functionality into MyParser
, so that the parsing would be just
private void parseResponse(String response) {
new MyParser(response).parse();
}
The parser would have fields like
private final Map<String, Map<Integer, String>> primaryData;
so that I could write
void parseDatacenter() {
String datacenter = readTill('\n');
skip("\n\tTotalNumberOfServers:");
int totalNumberOfServers = readInt();
skip("\n\tprimary{");
primaryData.put(datacenter, readMap());
skip("\n\t");
}
Map<String, String> readMap() {
Map<String, String> result = new HashMap<>();
while (true) {
String key = readTillAndSkip('=');
String value = readTillOneOf("},");
map.put(key, value);
if (lookingAt('}') {
break;
}
skip(", ");
}
return result;
}
As an example see this simple method
String readTillOneOf(String terminators) {
int start = index;
while (terminators.indexOf(content.charAt(index)) > -1) {
++index;
}
return content.substring(start, index);
}
I ignored quite some details like parsing numbers and I can't tell how many parsing methods you'll need, but basically it's all pretty simple. Just skip what's fixed and process what you want while looking for a terminator.
It could get a bit more complicated if you needed to be error-tolerant, like allowing multiple spaces, but then you could let some of your methods skip over them. Or, if it gets difficult, use regexes. But I don't think it gets complicated.
Note that I'm unsure why your parser is slow. There's a lot of splitting and this is the probable cause, but it's just a guess.
I started to write a comment, but it became too long.
###Questions
- 200 ms is pretty close to eternity, how long is your input?
- Can you measure how long
parseResponse
alone takes? - Could you post your data? Someone may try harder to optimize.
###Potential slowness causes
I guess your String.split
could be the culprit (with more than one character a regex gets created; you should use
private static final Pattern COMMA_BLANK_PATTERN = Pattern.compile(", ");
for splitting and use split limit of 2. Probably faster could be using Guava's
private static final Splitter COMMA_BLANK_SPLITTER = Splitter.on(", ");
Even better would be piece-wise processing instead of splitting.
Note also that Integer.parseInt
is internationalized and therefore slightly slower than necessary. Probably unimportant.
###Review
if (response != null) {
As Mat's Mug said, this is wrong. I'm using Guava's Preconditions
with a static import writing simply
checkNotNull(response);
This makes it fail-fast rather than hiding the problem to bite you later.
String tableString = map.split(":")[1];
Map<Integer, String> table = new HashMap<Integer, String>();
tableString = tableString.substring(1, tableString.length() - 1);
This is pretty confusing by squeezing something else between the two tableString
defining lines.
###Summary Overall, it's not bad. When speed is important, I'd go for something like
MyParser parser = new MyParser(response).skip("hasProcess=");
boolean hasProcess = parser.readBoolean();
parser.skip("\nversion=");
int version = parser.readInt();
while (parser.skipIfLookingAt("\nDATACENTER=")) {
parseDatacenter(parser, whatever...);
}
It's just an idea (damn similar to java.util.Scanner
, which isn't exactly known for speed), but I guess that string splitting is the problem (simply as I can't see anything else).
An example method could look like
MyParser skip(String prefix) {
for (int i=0; i<prefix.length(); ++i) {
if (content.charAt(index++) != prefix.charAt(i)) {
throw ...
}
}
}
where content
and index
are instance variables. This part doesn't need any substring creation at all (and gets a bit unreadable because of this). Warning: Doing something like content = content.substring(index)
(so you could use startsWith
) would make it clearer but terribly slow as you'd copy a big part of the string a lot of times.