严防Math.abs()返回负数

最近有个线上spark streaming程序跑着跑着就挂了,调查了一番,发现了一个平时大家都不太注意的问题。

看了日志,抛出的异常如下:

java.lang.ArrayIndexOutOfBoundsException: -2
      at com.xiaomi.poppy.hbase.HBaseUtil.getHashPrefix(HBaseUtil.java:58)
     at com.xiaomi.xmpush.spark.hbase.HBaseProcessor.buildRowKey(HBaseProcessor.java:220)
    at com.xiaomi.xmpush.spark.hbase.HBaseProcessor.buildRequest(HBaseProcessor.java:203)
  at com.xiaomi.xmpush.spark.hbase.HBaseProcessor.writeCounter(HBaseProcessor.java:126)
        at com.xiaomi.xmpush.main.PushSparkStatMain$4$1.call(PushSparkStatMain.java:102)
   at com.xiaomi.xmpush.main.PushSparkStatMain$4$1.call(PushSparkStatMain.java:97)
       at org.apache.spark.api.java.JavaRDDLike$$anonfun$foreach$1.apply(JavaRDDLike.scala:330)
  at org.apache.spark.api.java.JavaRDDLike$$anonfun$foreach$1.apply(JavaRDDLike.scala:330)
     at scala.collection.Iterator$class.foreach(Iterator.scala:727)
  at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
    at org.apache.spark.rdd.RDD$$anonfun$foreach$1$$anonfun$apply$28.apply(RDD.scala:894)
  at org.apache.spark.rdd.RDD$$anonfun$foreach$1$$anonfun$apply$28.apply(RDD.scala:894)
        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1854)
  at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1854)
    at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66)

找到出错代码如下:

    public static String getHashPrefix(String input, int length) {
        int hash = Math.abs(getHashCodeForString(input));
        final int radix = CHAR_MAP.length;
        StringBuffer sb = new StringBuffer();
        for (int index = 0; index < length; ++index) {
            sb.append(CHAR_MAP[hash % radix]);
            hash /= radix;
        }
        return sb.toString();
    }

具体来说就是sb.append(CHAR_MAP[hash % radix]);这句中的hash % radix得到了一个负数(-2),导致数组越界。

好奇怪啊,这里的hash是一个绝对值,按理来说是非负数才对,而radix是一个数组的长度,已经固定为62了,hash % radix怎么可能为负数。

先别下结论,我们首先知道的是:模运算的值如果为负数,则两个运算数必须要有一个为负数,并且我们可以百分之百肯定radix不可能为负数,所以按照神探福尔摩斯所说的:排除一切不可能的,剩下的即使再不可能,那也是真相,那我们就先把『真相』的帽子戴在这个绝对值的头上。

去看了一下JDK中关于Math.abs的文档:

 /**
     * Returns the absolute value of an {@code int} value.
     * If the argument is not negative, the argument is returned.
     * If the argument is negative, the negation of the argument is returned.
     *
     * <p>Note that if the argument is equal to the value of
     * {@link Integer#MIN_VALUE}, the most negative representable
     * {@code int} value, the result is that same value, which is
     * negative.
     *
     * @param   a   the argument whose absolute value is to be determined
     * @return  the absolute value of the argument.
     */
    public static int abs(int a) {
        return (a < 0) ? -a : a;
    }

果然找到了真相:如果是对Integer(Long也一样)取绝对值,如果原值是Integer.MIN_VALUE,则得到的绝对值和原值相等,是一个负数。为什么呢?因为你看看abs的实现,它很简单,如果原值是正数,直接返回,如果是负数,就简单地在前面加个负号。然而Integer的范围是[-2147483648,2147483647],如果原值是最小的值取反之后不就越界了嘛。

空口无凭,我们做个试验:

可以看到,试验结果与现场完全吻合。

其实这个坑我很早以前就知道了,并不是什么稀奇的问题,但是这次还是犯错了,所以在此记录一下,严防以后再犯,也和大家分享一下,希望大家以后注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值