首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用于Set的addAll抛出java.lang.UnsupportedOperationException

用于Set的addAll抛出java.lang.UnsupportedOperationException
EN

Stack Overflow用户
提问于 2022-09-13 11:03:56
回答 1查看 186关注 0票数 0

使用JDK 11.0.3。我有以下代码片段:

代码语言:javascript
运行
复制
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()

代码语言:javascript
运行
复制
@Override
public Set<String> getCustomerPhoneNumbers(String customerId) {
    return backOfficeInfoClient.getCustByHashNo(customerId).getPropertyLOVs()
            .flatMap(property -> property.getValues().values().stream())
            .collect(Collectors.toSet());
}

但是,它未能做到:

代码语言:javascript
运行
复制
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)

如果我更新如下:

代码语言:javascript
运行
复制
var allNumbersSet = new HashSet<>(customerInfoService.getCustomerPhoneNumbers(bankCustomerId));

现在很好用。

上面的代码用法有什么问题?你能解释一下为什么会出现这种情况吗?

此方法调用被调用Hazelcast缓存-前后包围。正如在评论中提到的,这可能是造成这种行为的一个原因:

缓存的值使用不可变集合表示,这是有意义的,因为这允许共享而不需要防御性副本。

解决方案:

找到了如何重写此逻辑并在不合并两个集合的情况下完成该操作的方法:

代码语言:javascript
运行
复制
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());
    }
}

再考虑一下这个逻辑:

代码语言:javascript
运行
复制
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()在不同条件下的确切工作方式是非常有用的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-13 11:31:52

根据Javadoc of Collectors.toSet()

返回的集合的类型、可更改性、可序列化性或线程安全性没有保证。

因此,如果您需要一个可变集,您应该自己创建一个集合,以确保它是可变的。

您可以使用所拥有的复制构造函数(new HashSet<>(...) -不要忘记<>)来实现这一点,也可以使用:

Collectors.toCollection(HashSet::new)

作为收集器,如链接的Javadoc所述。

但是,请注意,更多的streams方法是将两个流连接在一起:

代码语言:javascript
运行
复制
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());
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73701845

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档