首页 > 解决方案 > ArrayList::new 在 map 的 computeIfAbsent 方法中比 k->new ArrayList<>() 花费更多时间

问题描述

我需要根据对象的某些成员对对象列表进行分组。让我通过一个例子来展示我想要实现的需求:

我有一个UserStage类:

public class UserStage {

    private Integer userId;
    private Integer stageId;
    private LocalDateTime modifiedOn;

   //getter, setter and toString methods
}

现在假设我有一个List<UserStage>可以为同一个 userId 拥有多个对象的对象。我们称之为userStageDataList

我想按 userId 对这个列表进行分组,以便我可以获得特定 userId 的所有记录的列表。这可以通过不同的方法来实现,但我试图实现的方法如下:

Map<Integer, List<UserStage>> userWiseStageList = new HashMap<>();
        
for (UserStage userStage : userStageDataList) {
    userWiseStageList.computeIfAbsent(userStage.getUserId(), ArrayList::new).add(userStage);
}

但令我惊讶的是,对于列表中的 75 个项目,执行此操作需要持续大约 3000 毫秒。有时它会抛出java.lang.OutOfMemoryError: Java heap space.

当我替换为时ArrayList::newk -> new ArrayList<>()现在只需要 3 到 4 毫秒即可创建分组。

我尝试groupingBy使用streams,并且对于同一个列表,它也在 5-7 毫秒内很好地执行。

我使用以下代码来测量执行时间:

StopWatch watch = new StopWatch();
watch.start();
// here goes map creation code as shown above
logger.info("\n\n\n time taken for creating the map = {}",watch.getTime(TimeUnit.MILLISECONDS));

我在这里错过了什么吗?任何建议,将不胜感激!

谢谢。

标签: javaarraylistlambdajava-8hashmap

解决方案


  1. map.computeIfAbsent(k, func)如果没有 key ,将func使用参数调用。kmapk
  2. 函数ArrayList::new接受一个参数initialCapacity,该参数将是 List 的后备数组的初始大小

因此,在您的情况下,如果地图中不存在 userId 1000,则程序将执行new ArrayList<>(1000)以创建由Object[]size支持的 ArrayList 1000。这就是为什么它需要更长的时间并且有时会消耗所有 VM 内存的原因。


推荐阅读