java - 尝试将嵌套 for 循环转换为 Java 8 流时出错

标签 java java-8 java-stream

我正在尝试将嵌套的 for 循环转换为 Java 8 流。

循环实现

private Set<String> getAllowRolesFromInheritedPolicy(String userId, List<Policy> allowedPoliciesThisCustomer) {

    Set<String> allowedRolesThisUser = Sets.newHashSet();

    for (Policy policy : allowedPoliciesThisCustomer) {
        Map<String, Role> roles = policy.getRoles();
        for (Role role : roles.values()) {
            if (role.getUsers().contains(userId)) {
                allowedRolesThisUser.add(role.getRoleName());                   
            }
        }
    }

在代码中role.getUsers()返回 List<String> .

流式实现

我正在尝试将 for 循环更改为 java 8 流:

Set<String> allowedRolesThisUser = Sets.newHashSet(allowedPoliciesThisCustomer.stream()
                                       .map(policy -> policy.getRoles().values())
                                       .filter(role -> role.getUsers().contains(userId))
                                       .collect(Collectors.toList()));

但是编译器说:

error: cannot find symbol : .filter(role -> role.getUsers().contains(userId))
symbol:   method getUsers(), location: variable role of type Collection<Role>

Role具有函数 getUsers , Collection<Role>才不是。我应该怎么做才能使此转换正确?

谢谢。

最佳答案

解决方案

private static Set<String> streamImplementation(final String userId, 
  final List<Policy> allowedPoliciesThisCustomer) {

  return allowedPoliciesThisCustomer.stream()
    .map(Policy::getRoles)
    .map(Map::values)
    .flatMap(Collection::stream)
    .filter(r -> r.getUsers().contains(userId))
    .map(Role::getRoleName)
    .collect(Collectors.toSet());
}

解释

当您获取 map 中的值时,您需要将 map 展平。这是在以下两行中完成的

.map(Map::values)
.flatMap(Collection::stream)

这确保类型 Role 被正确传递。

完整 SSCCE

package com.stackoverflow;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static me.karun.Policy.policy;
import static me.karun.Role.role;
import static org.assertj.core.api.Assertions.assertThat;

public class PoliciesTest {

  @Test
  public void streamImplementation_whenComparedWithALoopImplementation_thenShouldReturnTheSameResult() {
    final String userId = "user-1";
    final Role role1 = role("role-1", "user-1", "user-2");
    final Role role2 = role("role-2", "user-1", "user-3");
    final Role role3 = role("role-3", "user-2", "user-3");
    final Role role4 = role("role-4", "user-3", "user-4");
    final List<Policy> allowedPoliciesThisCustomer = asList(
      policy(role1, role2),
      policy(role3, role4)
    );
    final Set<String> oldResult = loopImplementation(userId, allowedPoliciesThisCustomer);
    final Set<String> newResult = streamImplementation(userId, allowedPoliciesThisCustomer);

    assertThat(newResult).isEqualTo(oldResult);
  }

  private static Set<String> streamImplementation(final String userId, final List<Policy> allowedPoliciesThisCustomer) {
    return allowedPoliciesThisCustomer.stream()
      .map(Policy::getRoles)
      .map(Map::values)
      .flatMap(Collection::stream)
      .filter(r -> r.getUsers().contains(userId))
      .map(Role::getRoleName)
      .collect(toSet());
  }

  private static Set<String> loopImplementation(final String userId, final List<Policy> allowedPoliciesThisCustomer) {
    final Set<String> allowedRolesThisUser = new HashSet<>();

    for (final Policy policy : allowedPoliciesThisCustomer) {
      final Map<String, Role> roles = policy.getRoles();
      for (final Role role : roles.values()) {
        if (role.getUsers().contains(userId)) {
          allowedRolesThisUser.add(role.getRoleName());
        }
      }
    }

    return allowedRolesThisUser;
  }
}

@RequiredArgsConstructor
@Getter
class Policy {
  private final Map<String, Role> roles;

  static Policy policy(final Role... roles) {
    final Map<String, Role> rolesMap = stream(roles)
      .collect(toMap(Role::getRoleName, identity()));

    return new Policy(rolesMap);
  }
}

@RequiredArgsConstructor
@Getter
class Role {
  private final List<String> users;
  private final String roleName;

  static Role role(final String roleName, final String... users) {
    return new Role(asList(users), roleName);
  }
}

关于java - 尝试将嵌套 for 循环转换为 Java 8 流时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45093071/

相关文章:

java - 如何选择Lucene中WordDelimiterFilter使用的分隔符?

java - 如何将 groupingBy 放入 Map 并更改键类型

java - 将字母表添加到 Java 列表

java-8 - Optional.ifAbsentThrow()?

java - 使用专门类型进行 Scala 优化 : sum with longs

java - Java 8 流惰性在实践中没有用吗?

Java - 如何将 Scala Stream 转换为 Java Stream?

java - 使用来自另一个程序的信息

Java透明JFrame、JVLC和java.awt.Canvas

java - 使用函数式风格合并两个数组