Хорошо известно что производительность String.format() ужасна. Я вижу большие возможные улучшения в моем (и, вероятно, очень распространенном) типичном случае. Я печатаю ту же структуру данных много раз. Представьте себе структуру типа "x:% d y:% d z:% d". Я ожидаю, что главная проблема с String.format() заключается в том, что он должен всегда анализировать строку форматирования. Мой вопрос: есть ли какой-то готовый класс, который позволит читать строку форматирования только один раз, а затем позволить быстро выдавать строку при заполнении переменных параметров? Использование должно выглядеть следующим образом:
PreString ps = new PreString("x:%d y:%d z:%d");
String s;
for(int i=0;i<1000;i++){
s = ps.format(i,i,i);
}
Я знаю, что это возможно - следующий мой быстрый и грязный пример, который делает то, о чем я говорю, и примерно на 10 раз быстрее на моей машине:
public interface myPrintable{
boolean isConst();
String prn(Object o);
String prn();
}
public class MyPrnStr implements myPrintable{
String s;
public MyPrnStr(String s){this.s =s;}
@Override public boolean isConst() { return true; }
@Override public String prn(Object o) { return s; }
@Override public String prn() { return s; }
}
public class MyPrnInt implements myPrintable{
public MyPrnInt(){}
@Override public boolean isConst() { return false; }
@Override public String prn(Object o) { return String.valueOf((Integer)o); }
@Override public String prn() { return "NumMissing"; }
}
public class FastFormat{
myPrintable[] obj = new myPrintable[100];
int objIdx = 0;
StringBuilder sb = new StringBuilder();
public FastFormat() {}
public void addObject(myPrintable o) { obj[objIdx++] = o; }
public String format(Object... par) {
sb.setLength(0);
int parIdx = 0;
for (int i = 0; i < objIdx; i++) {
if(obj[i].isConst()) sb.append(obj[i].prn());
else sb.append(obj[i].prn(par[parIdx++]));
}
return sb.toString();
}
}
Он используется следующим образом:
FastFormat ff = new FastFormat();
ff.addObject(new MyPrnStr("x:"));
ff.addObject(new MyPrnInt());
ff.addObject(new MyPrnStr(" y:"));
ff.addObject(new MyPrnInt());
ff.addObject(new MyPrnStr(" z:"));
ff.addObject(new MyPrnInt());
for (int i = 0; i < rpt; i++) {
s = ff.format(i,i,i);
}
когда я сравниваю с
long beg = System.nanoTime();
for (int i = 0; i < rpt; i++) {
s = String.format("x:%d y:%d z:%d", i, i, i);
}
long diff = System.nanoTime() - beg;
Для предварительного форматирования итерации 1e6 улучшается результат в ~ 10:
time [ns]: String.format() (+90,73%) 3 458 270 585
time [ns]: FastFormat.format() (+09,27%) 353 431 686
[EDIT]
Как Стив Chaloner ответил, что есть MessageFormat, который довольно делая то, что я хочу. Поэтому я попробовал код:
MessageFormat mf = new MessageFormat("x:{0,number,integer} y:{0,number,integer} z:{0,number,integer}");
Object[] uo = new Object[3];
for (int i = 0; i < rpt; i++) {
uo[0]=uo[1]=uo[2] = i;
s = mf.format(uo);
}
И это быстрее только в 2 раза. Не фактор 10, который я надеялся. Снова см. Измерение для 1-й итерации (JRE 1.8.0_25-b18 32bit):
time [s]: String.format() (+63,18%) 3.359 146 913
time [s]: FastFormat.format() (+05,99%) 0.318 569 218
time [s]: MessageFormat (+30,83%) 1.639 255 061
[EDIT2]
Как Slanec ответил org.slf4j.helpers.MessageFormatter. (Я попробовал библиотечную версию slf4j-1.7.12
)
Я попытался сравнить код:
Object[] uo2 = new Object[3];
beg = System.nanoTime();
for(long i=rpt;i>0;i--){
uo2[0]=uo2[1]=uo2[2] = i;
s = MessageFormatter.arrayFormat("x: {} y: {} z: {}",uo2).getMessage();
}
с кодом для MessageFormat, указанным выше в разделе [EDIT]. Я получил следующие результаты для его циклирования 1M раз:
Time MessageFormatter [s]: 1.099 880 912
Time MessageFormat [s]: 2.631 521 135
speed up : 2.393 times
Итак, MessageFormatter - лучший ответ до сих пор, но мой простой пример все же немного быстрее... Итак, любое готовое ускорение предложения библиотеки?