2017-08-09 45 views
0

我有一个项目列表和一个存储产品信息和它的项目数据的地图。 DB中大约有150k个项目和大约200k个产品(每个产品大约有1000到2000个项目映射到它)。 。在性能方面实现最佳功能

我需要才是最重要的产品每一个项目出现在量的函数这是我所实现的功能:

public Map<Integer, Integer> getProductsNumberForItem(List<Item> itemsList, 
     Map<Integer, Map<Item, Integer>> itemsAmount) { 
    Map<Integer, Integer> result = new HashMap<>(); 
    for (Item i : itemsList) { 
     int count = 0; 
     for (Map<Item, Integer> entry : itemsAmount.values()) { 
      if (entry.containsKey(i)) { 
       count++; 
      } 
     } 
     result.put(i.getID(), count); 
    } 
    return result; 
} 

它可以在我的测试数据库,里面有少量的数据不错,但当我在真实数据上运行它时,它需要很多时间(例如:它已经运行了一个小时,仍然没有完成)。从逻辑上来看它清楚,我基本上执行了太多的操作,但不知道如何优化。

任何建议表示赞赏。

+4

您使用的是关系型数据库吗?如果你是那么你应该能够做大部分的工作。 – mrmcgreg

+0

将聚合推送到数据库。 –

+0

@mrmcgreg是的,这只是我想到的,我可以检索映射产品集的大小。 – Cap

回答

2

有两种方式:

  • 最有效的:做计算在数据库中执行的查询。
    随着count()聚合和group by子句,你应该得到一个更好的结果,因为整个处理将由设计/优化来执行的DBMS执行。

  • 效率较低,但您可以试一试:像现在一样检索数据并使用多线程。
    随着Java 8 parallelStream(),你可能会得到一个可以接受的结果,而没有麻烦来自己处理同步。

+0

我已经实现了它,只需更正实体内的映射。不过谢谢。 – Cap

+0

没问题。但是,如果关系是懒惰的,它将为每个产品执行不同的查询。这不是有效的。 – davidxxx

+0

哦,我没有指定,如果它是急切或懒惰加载。是否有可能确定默认情况下的设置? – Cap

0

最好的选择就是这种计算委托给分贝,避免了需要的所有数据传输到你的应用服务器。

如果这不是一个选项,那么肯定可以改进你当前的算法。现在,对于列表中的每个项目,您正在循环所有产品;这是指数成本。

你能做到这一点(使用流,因为ressoning更容易在我看来,以遵循,也允许添加一些改进,但同样也没有他们实现):

Stream<Item> productsItemsStream = itemsAmount.values().stream().flatMap(p -> p.keySet().stream()); 
Map<Item,Long> countByItemFound = productsItemsStream.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()); 
Map<Integer, Integer> result = itemsList.stream().collect(Collectors.toMap(Item::getID, i -> countByItemFound.getOrDefault(i.getID(), 0L).intValue())); 

通过这种方法,你会做一个完整的传递给产品项目。然后再传递给项目列表。这是线性成本。

Specificto流,你可以尝试启用并行性(向我的解决方案中添加parallelStream),但它并不完全授予性能提高;取决于几个因素。我希望能够看到提议的解决方案的性能,并且如果需要的话,可以在您的方案中使用和不使用parallelStream来分析性能。