问题描述:

最近遇到一个问题,在程序中,有一个百万级别的商品集合,需要过滤掉商品黑名


单,这个黑名单由运营同学手工配置,从原来的几个到几百个,再到上千个,导致现


在程序在过滤这块耗时太长。如何使得程序的过滤运行时间缩短呢?


分析:


比如,我们的商品集合是Map<ID,Product> skuMap,ID为商品的ID,Product是商品类型


对象,大小是100W+,商品黑名单列表是每隔10MIN去读取一次形成long[] skus,数组的


元素放置的就是商品ID,数组大小是几千级别。



过滤方案:


方案一:


伪码:


for(Product p : skuMap){


  for(Long id : skus){


    if(p.getId() == id){

      //此商品被过滤掉

      p.setFlag(false);

     }

   }

}


这种方案,是最初的写法,想法比较简单,就是2层FOR循环。


但是,随着配置的黑名单越来越多,从最初的几个到几百个,最后到几千个级别,导致


这段代码运行时间发生波动,并且越来越长!



方案二:


如何缩短时间呢?


对于方案一,而言,100W+的商品中的每一个商品都必须对skus进行全部遍历才能完成过


滤逻辑,导致循环次数是 100W+ * 1000+ 级别,也就是10亿+的级别,循环的次数实在


是太多了!要缩短循环次数,才能缩短时间!


伪码:


Arrays.sort(skus);


for(Product p : skuMap){


   int result = Arrays.binarySearch(skus,id);


   if(result < 0){

      p.setFlag(false);

   }

}


如果我们在拿到skus后,对skus内部的ID进行一次排序,然后每个商品对skus进行二分


查找。要知道1000+的黑名单,对于二分查找而言,最多查找11次就可以找到!也就是在


最坏的情况下,我们需要循环的次数变成了100W+ * 11,即1000W+的循环次数!


这样方案二,一下子将10亿+的循环次数缩短至1000W+。


虽然如此,但是程序的过滤时间,日志打印,竟然是300S+,这也无法接受!



方案三:


我们为什么要拿skuMap取遍历skus,为什么不用黑名单去过滤商品集合呢?


伪码:


for(long sku : skus){


  if(skuMap.containsKey(sku)){

     skuMap.get(sku).setFlag(false);

  }


}


外层循环,变成了黑名单,在内部要知道我们用的是HASHMAP查找,实际上,一下子,循环次数变成了1000+。程序的过滤时间也缩短至几秒钟!