I have a pipe delimited file which I parse to get system options. The environment is sensitive to heap allocation and we are trying to avoid garbage collection.
Below is the code I am using to parse the pipe delimited string. This function is called about 35000 times. I was wondering if there was a better approach that doesn't create as much memory churn.
static int countFields(String s) {
int n = 1;
for (int i = 0; i < s.length(); i++)
if (s.charAt(i) == '|')
n++;
return n;
}
static String[] splitFields(String s) {
String[] l = new String[countFields(s)];
for (int pos = 0, i = 0; i < l.length; i++) {
int end = s.indexOf('|', pos);
if (end == -1)
end = s.length();
l[i] = s.substring(pos, end);
pos = end + 1;
}
return l;
}
EDIT 1, about java version:
For business reasons we are stuck at JDK 1.6.0_25.
EDIT 2 about String and String[] usage:
The String[] is used to perform the system setup logic. Basically, if String[0].equals("true") then enable debugging. That's the usage pattern
EDIT 3 about garbage collected objects:
The input String and the String[] are eventually GC'd. The input String is a single line from system setup file which is GC'd after the entire file is processed and the String[] is GC'd after the entire line has been processed.
EDIT - SOLUTION:
This is a combo of Peter Lawrey and zapl's solutions. Also, this class is NOT thread safe.
public class DelimitedString {
private static final Field EMPTY = new Field("");
private char delimiter = '|';
private String line = null;
private Field field = new Field();
public DelimitedString() { }
public DelimitedString(char delimiter) {
this.delimiter = delimiter;
}
public void set(String line) {
this.line = line;
}
public int length() {
int numberOfFields = 0;
if (line == null)
return numberOfFields;
int idx = line.indexOf(delimiter);
while (idx >= 0) {
numberOfFields++;
idx = line.indexOf(delimiter, idx + 1);
}
return ++numberOfFields;
}
public Field get(int fieldIndex) {
if (line == null)
return EMPTY;
int currentField = 0;
int startIndex = 0;
while (currentField < fieldIndex) {
startIndex = line.indexOf(delimiter, startIndex);
// not enough fields
if (startIndex < 0)
return EMPTY;
startIndex++;
currentField++;
}
int endIndex = line.indexOf(delimiter, startIndex);
if (endIndex == -1)
endIndex = line.length();
fieldLength = endIndex - startIndex;
if (fieldLength == 0)
return EMPTY;
// Populate field
for (int i = 0; i < fieldLength; i++) {
char c = line.charAt(startIndex + i);
field.bytes[i] = (byte) c;
}
field.fieldLength = fieldLength;
return field;
}
@Override
public String toString() {
return new String(line + " current field = " + field.toString());
}
public static class Field {
// Max size of a field
private static final int DEFAULT_SIZE = 1024;
private byte[] bytes = null;
private int fieldLength = Integer.MIN_VALUE;
public Field() {
bytes = new byte[DEFAULT_SIZE];
fieldLength = Integer.MIN_VALUE;
}
public Field(byte[] bytes) {
set(bytes);
}
public Field(String str) {
set(str.getBytes());
}
public void set(byte[] str) {
int len = str.length;
bytes = new byte[len];
for (int i = 0; i < len; i++) {
byte b = str[i];
bytes[i] = b;
}
fieldLength = len;
}
public char charAt(int i) {
return (char) bytes[i];
}
public byte[] getBytes() {
return bytes;
}
public int length() {
return fieldLength;
}
public short getShort() {
return (short) readLong();
}
public int getInt() {
return (int) readLong();
}
public long getLong() {
return readLong();
}
@Override
public String toString() {
return (new String(bytes, 0, fieldLength));
}
// Code taken from Java class Long method parseLong()
public long readLong() {
int radix = 10;
long result = 0;
boolean negative = false;
int i = 0, len = fieldLength;
long limit = -Long.MAX_VALUE;
long multmin;
int digit;
if (len > 0) {
char firstChar = (char) bytes[0];
if (firstChar < '0') { // Possible leading "-"
if (firstChar == '-') {
negative = true;
limit = Long.MIN_VALUE;
} else
throw new NumberFormatException("Invalid leading character.");
if (len == 1) // Cannot have lone "-"
throw new NumberFormatException("Negative sign without trailing digits.");
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(bytes[i++], radix);
if (digit < 0)
throw new NumberFormatException("Single digit is less than zero.");
if (result < multmin)
throw new NumberFormatException("Result is less than limit.");
result *= radix;
if (result < limit + digit)
throw new NumberFormatException("Result is less than limit plus new digit.");
result -= digit;
}
} else {
throw new NumberFormatException("Called readLong with a length <= 0. len=" + len);
}
return negative ? result : -result;
}
}
}