有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java8流API中的条件检查

我有以下现有的实现

CompletableFuture<Employee>[] employeeDetails =
            empIds.stream().map(empId ->
                employeeService.employeeDetails(Integer.valueOf(empId)))
            .filter(Objects::nonNull)
            .toArray(CompletableFuture[]::new);

在这段代码中,我需要在HashMap中检查empId是否已经存在,如下所示——如果empId在HashMap中不存在,则调用服务并将其放入HashMap中以备将来使用

Map<String, CompletableFuture<Employee>[]> employeeCache = new HashMap<>();

..........

CompletableFuture<Employee>[] employeeDetails =
            empIds.stream().map(empId ->

            //Here I need to check in HashMap that empId is present or not if present then fetch from Map instead of calling service.

                employeeService.employeeDetails(Integer.valueOf(empId)))
            .filter(Objects::nonNull)
            .toArray(CompletableFuture[]::new);

如何添加if签入stream()api,如上图所示,如果存在,则从Map获取


共 (3) 个答案

  1. # 1 楼答案

    你可以使用^{}来实现你想要做的事情:

    Map<String, CompletableFuture<Employee>[]> employeeCache = new HashMap<>();
    
    CompletableFuture<Employee>[] employeeDetails = empIds.stream()
                    .map(empId -> employeeCache
                            .computeIfAbsent(empId, k -> employeeService.employeeDetails(Integer.valueOf(empId))))
                            .filter(Objects::nonNull)
                            .toArray(CompletableFuture[]::new);
    
  2. # 2 楼答案

    如果您不再需要employeeCache映射,也就是说,如果您只将其用作Stream.map操作中的本地缓存,则可以通过通用实用程序方法将其提取出来:

    public static <T, U> Function<T, U> memoize(Function<T, U> f) {
        Map<T, U> m = new HashMap<>();
        return t -> m.computeIfAbsent(t, f);
    }
    

    这被称为memoization,是一种众所周知的技术,尤其是在函数式编程中

    然后,在示例中使用它,如下所示:

    CompletableFuture<Employee>[] employeeDetails = empIds
        .stream()
        .map(memoize(empId -> employeeService.employeeDetails(Integer.valueOf(empId))))
        .filter(Objects::nonNull)
        .toArray(CompletableFuture[]::new);
    

    正如您所看到的,这对调用方代码是完全透明的,它确实会将映射用作缓存

    编辑:请注意,我只使用了一个公共的^{,因为我注意到流是按顺序运行的。如果要并行运行流,应该将缓存更改为ConcurrentHashMap

  3. # 3 楼答案

    您可以在Stream.map操作中使用^{}(因为Map中缺少键将返回null)和^{}(在Map中不存在值时进行服务调用),如下所示:

    CompletableFuture<Employee>[] employeeDetails = empIds.stream()
            .map(empId -> Optional.ofNullable(employeeCache.get(empId))
                    .orElseGet(() -> employeeService.employeeDetails(Integer.valueOf(empId))))
            .filter(Objects::nonNull)
            .toArray(CompletableFuture[]::new);
    

    或者正如Aomine在评论中所建议的,您也可以简单地使用getOrDefault

    CompletableFuture<Employee>[] employeeDetails = empIds.stream()
            .map(empId -> employeeCache.getOrDefault(empId, 
                    employeeService.employeeDetails(Integer.valueOf(empId))))
            .filter(Objects::nonNull)
            .toArray(CompletableFuture[]::new);
    

    如果你也想放入缓存,你可以简单地使用^{}来放入,如果不存在的话:

    CompletableFuture<Employee>[] employeeDetails = empIds.stream()
            .map(empId -> employeeCache.putIfAbsent(empiId, employeeService.employeeDetails(Integer.valueOf(empId))))
            .filter(Objects::nonNull)
            .toArray(CompletableFuture[]::new);
    

    如果您想在从服务中删除内容的同时也更新缓存,您最好在此处不使用流:

    List<CompletableFuture<Employee>[]> list = new ArrayList<>();
    for (String empId : empIds) {
        CompletableFuture<Employee>[] completableFutures = employeeCache.putIfAbsent(employeeService.employeeDetails(Integer.valueOf(empId)));
        if (completableFutures != null) {
            list.add(completableFutures);
        }
    }
    CompletableFuture<Employee>[] employeeDetails = list.toArray(new CompletableFuture[0]);
    

    此外,所有这些(如果我是你的话)我都会使用Guava的^{},它甚至可以通过定制的CacheLoader实现在封面下提供类似的好处

    关于它的更多细节,你可以阅读他们的维基-CachesExplained