在性能方面优化功能的实现

时间:2017-08-09 20:23:04

标签: java optimization

我有一个项目列表和一个存储有关产品及其项目数据的信息的地图。数据库中有大约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;
}

它在我的测试数据库上工作正常,它有少量数据,但是当我在真实数据上运行时,它需要花费太多时间(例如:它已经运行了一个小时但仍然没有完成)。从逻辑的角度来看它清楚,我基本上执行了太多操作,但不确定如何优化。

任何建议表示赞赏。

2 个答案:

答案 0 :(得分:2)

您有两种方式:

  • 效率最高:在数据库中执行的查询中进行计算 使用count()聚合和group by子句,您应该获得更好的结果,因为整个处理将由设计/优化的DBMS执行。

    < / LI>
  • 效率较低,但您可以尝试一下:现在检索数据并使用多线程 使用Java 8 parallelStream(),您可以获得可接受的结果,而无需自己处理同步的麻烦。

答案 1 :(得分: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()));

使用这种方法,您将对产品项目进行一次完整传递。然后另一个传递到项目列表。这是线性成本。

特定于流,您可以尝试启用并行性(将parallelStream添加到我的解决方案中),但它并没有完全被授予大的性能提升;取决于几个因素。我会等待在提议的解决方案上看到性能,如果需要,还可以在您的方案中使用和不使用parallelStream来分析性能。