【问题标题】:addAll for Set throws java.lang.UnsupportedOperationException设置的 addAll 抛出 java.lang.UnsupportedOperationException
【发布时间】:2022-10-20 16:48:04
【问题描述】:

使用JDK 11.0.3。我有以下代码sn-p:

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 缓存包围 - 之前和之后。正如 cmets 所述,这可能是导致这种行为的原因:

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

解决方案:

找到了如何重写这个逻辑并在不合并两组的情况下做这些事情的方法:

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() 在不同条件下的工作原理确实非常有用。

【问题讨论】:

  • @sp00m import java.util.stream.Collectors;
  • 附带说明一下,无论如何改变参数都不是一个好主意,我会简单地实例化一个新的本地HashSet,就像您尝试过的那样,即使您设法使getCustomerPhoneNumbers 返回一个可变集。

标签: java exception set java-11 unsupportedoperation


【解决方案1】:

根据Javadoc of Collectors.toSet()

不保证返回的 Set 的类型、可变性、可序列化性或线程安全性。

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

您可以使用您拥有的复制构造函数(new HashSet&lt;&gt;(...) - 不要忘记&lt;&gt;)来执行此操作,或者您可以使用:

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());

【讨论】:

  • 即使collect(Collectors.toCollection(HashSet::new) UnsupportedOperationException 被抛出
  • 我在调试模式下检查 - 它为两种情况创建 ImmutableCollections#Set
  • @AndyTurner 我尝试创建一些演示样本。但是,Collectors.toSet() 返回了一个可修改的集合,它按预期工作。但是一个真正的项目失败了ImmutableCollections#Set
  • @catch23 也许,您正在进行一些代码检测,例如注入缓存设施?
  • 那么,这或许就是答案。缓存的值使用不可变集合表示,这是有道理的,因为它允许共享而不需要防御性副本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-28
  • 1970-01-01
  • 2012-12-09
相关资源
最近更新 更多