是否有比Integer.toString(myInt).getBytes(US_ASCII)更快的选择?

6

用户向我发送byte / short / int / long值。 我必须将其作为POST HTTP请求的一部分发送,并且必须将数字作为字符串发送。

因此,我现在执行以下操作:

//simplified version
byte[] data = Integer.toString(myInt).getBytes(US_ASCII);
sendPost(data);

我正在寻找更快的替代方案

Integer.toString(myInt).getBytes(US_ASCII);

由于这个流程创建了char[]Stringbyte[]对象,而我只需要byte[]。我想知道是否有更快/更好的替代方法。

1
@LutzHorn byte[] {31, 32, 33} 精确地转换为十六进制。 - Dmitriy Dumanskiy
然后你需要先将 int 转换为 String - user11044402
好的,这就是代码已经实现的内容。问题是如何在不创建中间对象的情况下寻找更快的替代方案,例如Integer.toBytesAsStringValue(myInt)。 - Dmitriy Dumanskiy
7
我不认为JDK中有这样的东西。你确定这是值得优化的代码吗? - user11044402
也许当您的输入经常相同时,可以进行一些缓存处理? - Matthias
显示剩余3条评论
4个回答

2
这个解决方案非常简单明了。
import java.util.Arrays; // Needed only for demo purposes

public class LongToBytes
{
  private static final byte ZERO = '0';
  private static final byte MINUS = '-';

  public static byte[] convert(long value)
  {
    // -------------------------------------
    // Manage some particular value directly
    // abs(Long.MIN_VALUE) remains negative
    // -------------------------------------
    if ((value >= 0) && (value < 10))
      return (new byte[]{(byte)(ZERO + value)});
    else if ((value > -10) && (value < 0))
      return (new byte[] {MINUS, (byte)(ZERO - value)});
    else if (value == Long.MIN_VALUE)
      return (Long.toString(value).getBytes());

    // -----------------------------------------------------------------
    // Initialize result
    // The longest value (Long.MIN_VALUE+1) is composed of 20 characters
    // -----------------------------------------------------------------
    byte[] array;
    array = new byte[20];

    // ---------------------------
    // Keep track of eventual sign
    // ---------------------------
    boolean negative;
    negative = (value < 0);
    if (negative)
      value = -value;

    // ----------------------
    // Fill array (backwards)
    // ----------------------
    int size;
    size = 0;
    while (value > 0)
    {
      array[size] = (byte)((value % 10) + ZERO);
      size++;
      value /= 10;
    }

    // -------------------
    // Add sign eventually
    // -------------------
    if (negative)
    {
      array[size] = MINUS;
      size++;
    }

    // -------------------------------------------------------------
    // Compose result, giving it the correct length and reversing it
    // -------------------------------------------------------------
    byte[] result;
    int    counter;
    result = new byte[size];
    for (counter = 0; counter < size; counter++)
      result[size - counter - 1] = array[counter];

    // ----
    // Done
    // ----
    return (result);

  } // convert

  public static void main(String[] args)
  {
    try
    {
      long value;
      value = Long.parseLong(args[0]);
      System.out.println(value);
      System.out.println(Arrays.toString(convert(value)));
    }
    catch (Exception exception)
    {
      exception.printStackTrace();
    }
  }

} // class LongToBytes

更新
我单独在循环中调用了这里上面的方法和Long.toString().getBytes(),并使用System.nanoTime()作为秒表对性能进行了测试。

传递0时,上述方法大约快500倍。
相对较小的值(在-10.000和10.000之间)的收益率约为60%。
巨大的值(接近Long.MIN_VALUE和Long.MAX_VALUE)的收益率约为40%。

更新2
针对特定值(在-9和9之间以及值Long.MIN_VALUE),情况略有改善。
我已更新该方法的实现。


0
这里有一个可能性。这种方法比使用Integer.toString()更加快速和一致。
   public static byte[] getBytes(long value) {
      int sign = 0;
      if (value < 0) {
         sign = 1;
         value = -value;
      }

      long temp = 10L;
      int chars = 1;
      for (int i = 1; i < 20; i++) {
         if (value < temp) {
            chars = i + sign;
            break;
         }
         temp *= 10;
      }


      byte[] bytes = new byte[chars];

      int i = 0;
      while (value > 0) {
         bytes[chars - i - 1] = (byte) ((value % 10) + '0');
         value /= 10;
         i++;
      }

      if (sign == 1) {
         bytes[0] = '-';
      }
      return bytes;
   }

@dmitriydumanskiy,我修改了数字计算。似乎比对数函数有了很大的改进。 - WJS

0
感谢大家的建议。我决定采用最简单的方式。我只是复制了Integer.toString()中的方法,并将其与最小改动内联到我的代码中。我没有进行精确的基准测试,但是对于我编写的JDBC驱动程序,由于这个更改,我获得了2倍的性能提升。以下是最终解决方案:
private static final byte[] DigitTens = {
        '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
        '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
        '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
        '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
        '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
        '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
        '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
        '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
        '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
        '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
};

private static final byte[] DigitOnes = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
};

public byte[] intAsStringToBytes(int i) {
    int size = stringSize(i);
    byte[] buf = new byte[size];
    getChars(i, size, buf);
    return buf;
}

private static int stringSize(int x) {
    int d = 1;
    if (x >= 0) {
        d = 0;
        x = -x;
    }
    int p = -10;
    for (int i = 1; i < 10; i++) {
        if (x > p) {
            return i + d;
        }
        p = 10 * p;
    }
    return 10 + d;
}

private static void getChars(int i, int index, byte[] buf) {
    int q, r;
    int charPos = index;

    boolean negative = i < 0;
    if (!negative) {
        i = -i;
    }

    // Generate two digits per iteration
    while (i <= -100) {
        q = i / 100;
        r = (q * 100) - i;
        i = q;
        buf[--charPos] = DigitOnes[r];
        buf[--charPos] = DigitTens[r];
    }

    // We know there are at most two digits left at this point.
    q = i / 10;
    r = (q * 10) - i;
    buf[--charPos] = (byte) ('0' + r);

    // Whatever left is the remaining digit.
    if (q < 0) {
        buf[--charPos] = (byte) ('0' - q);
    }

    if (negative) {
        buf[--charPos] = (byte) '-';
    }
}

1
避免不必要的对象显然可以提高性能。在Clickhouse JDBC驱动程序的上下文中,我还建议考虑不为每个int或其他变量创建单独的byte[]数组。使用一个byte[]数组(就像在StringBuilder中一样)来存储整个SQL请求,并使用“append”样式来填充它。 - egorlitvinenko
是的,这是我目前正在clickhouse4j中工作的内容。 - Dmitriy Dumanskiy

0

只是一个想法

int digits = getNumberOfDifits(number);

byte[] out = new byte[digits];

for (int j = digits - 1; j >= 0; j--) {
    int rem = number % 10;
    out[j] = getByteForDigit(rem);
    number = number / 10;
}

return out

堆中没有辅助对象,所有内容都在栈中处理


实际上,在Integer.getChars()内部也做了类似的事情,但它更加复杂。我想我会采用这种方式,并复制JDK方法并应用于我的需求。 - Dmitriy Dumanskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接