naz*_*art 1 java exception set unsupportedoperation java-11
使用JDK 11.0.3。我有以下代码片段:
Set<String> allNumbersSet = customerInfoService.getCustomerPhoneNumbers(bankCustomerId);
additionalInformation
        .map(info -> info.get(BANK_PE_CUSTOMER_ID_KEY))
        .filter(StringUtils::isNotEmpty)
        .ifPresent(id -> allNumbersSet.addAll(customerInfoService.getCustomerPhoneNumbers(id))); // fails here
获取电话号码的地方就是Collectors.toSet():
@Override
public Set<String> getCustomerPhoneNumbers(String customerId) {
    return backOfficeInfoClient.getCustByHashNo(customerId).getPropertyLOVs()
            .flatMap(property -> property.getValues().values().stream())
            .collect(Collectors.toSet());
}
但是,它失败了:
java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71)
    at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.addAll(ImmutableCollections.java:76)
    at service.impl.UserManagementServiceImpl.lambda$validateNewLogin$3(UserManagementServiceImpl.java:69)
如果我像下面这样更新:
var allNumbersSet = new HashSet<>(customerInfoService.getCustomerPhoneNumbers(bankCustomerId));
现在效果很好。
上面的代码使用有什么问题吗?您能解释一下为什么会出现这种情况吗?
此方法调用由调用 Hazelcast 缓存包围 - 之前和之后。正如评论中提到的,这可能是这种行为的原因:
缓存的值使用不可变集合表示,这是有道理的,因为这允许共享而无需防御性副本
解决方案:
找到了如何重写此逻辑并在不合并两组的情况下执行该操作的方法:
var numbersSet = customerInfoService.getCustomerPhoneNumbers(id);
if (!numbersSet.contains(newLogin)) {
    var peNumbersSet = additionalInformation
            .map(info -> info.get(BANK_PE_CUSTOMER_ID_KEY))
            .filter(StringUtils::isNotEmpty)
            .map(customerInfoService::getCustomerPhoneNumbers)
            .orElseGet(Collections::emptySet);
    if (!peNumbersSet.contains(newLogin)) {
        throw new ProcessException(ServerError.WRONG_LOGIN_PROVIDED.errorDTO());
    }
}
稍微重新思考一下这个逻辑:
var additionalInformation = Optional.ofNullable(user.getAdditionalInformation());
var phoneNumbers = new HashSet<String>();
additionalInformation
        .map(i -> i.get(BANK_CUSTOMER_ID_KEY))
        .filter(StringUtils::isNotEmpty)
        .map(customerInfoService::getCustomerPhoneNumbers)
        .ifPresent(phoneNumbers::addAll);
additionalInformation
        .map(i -> i.get(BANK_PE_CUSTOMER_ID_KEY))
        .filter(StringUtils::isNotEmpty)
        .map(customerInfoService::getCustomerPhoneNumbers)
        .ifPresent(phoneNumbers::addAll);
if (!phoneNumbers.contains(newLogin)) {
    throw new MetryusProcessException(AuthServerError.WRONG_LOGIN_PROVIDED.errorDTO());
}
然而,了解Collectors.toSet()在不同条件下到底如何工作确实非常有用。
对于返回的 Set 的类型、可变性、可序列化性或线程安全性没有任何保证。
因此,如果您需要一个可变集,您应该自己创建一个,以确保它是可变的。
您可以使用您拥有的复制构造函数(new HashSet<>(...)- 不要忘记<>)来做到这一点,或者您可以使用:
Collectors.toCollection(HashSet::new)
作为收集器,如链接的 Javadoc 中所述。
但是,请注意,一种更 Stream-y 的方法是连接两个流:
Set<String> someNumbersSet = customerInfoService.getCustomerPhoneNumbers(bankCustomerId);
Set<String> allNumbersSet =
    Stream.concat(
        someNumbersSet.stream(),
        additionalInformation
                .map(info -> info.get(BANK_PE_CUSTOMER_ID_KEY))
                .filter(StringUtils::isNotEmpty)
                .map(customerInfoService::getCustomerPhoneNumbers)
                .stream()
                .flatMap(Collection::stream))
          .collect(Collectors.toSet());