chinabugotech/hutool · error · UnsupportedOperationException
Comparator ordering cannot be changed after the first compar
Error message
Comparator ordering cannot be changed after the first comparison is performed
What it means
Thrown by ComparatorChain when attempting to modify the chain (addComparator, setComparator, setForwardSort, setReverseSort) after the first call to compare(). The chain 'locks' itself on the first comparison to ensure consistent ordering throughout a sort operation. This is a state-transition error: the object transitions from mutable to immutable after first use.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/comparator/ComparatorChain.java:341
if (object.getClass().equals(this.getClass())) {
final ComparatorChain<?> otherChain = (ComparatorChain<?>) object;
//
return Objects.equals(this.orderingBits, otherChain.orderingBits)
&& this.chain.equals(otherChain.chain);
}
return false;
}
//------------------------------------------------------------------------------------------------------------------------------- Private method start
/**
* 被锁定时抛出异常
*
* @throws UnsupportedOperationException 被锁定抛出此异常
*/
private void checkLocked() {
if (lock == true) {
throw new UnsupportedOperationException("Comparator ordering cannot be changed after the first comparison is performed");
}
}
/**
* 检查比较器链是否为空,为空抛出异常
*
* @throws UnsupportedOperationException 为空抛出此异常
*/
private void checkChainIntegrity() {
if (chain.size() == 0) {
throw new UnsupportedOperationException("ComparatorChains must contain at least one Comparator");
}
}
//------------------------------------------------------------------------------------------------------------------------------- Private method start
}
View on GitHub (pinned to 8870454b2a)
Solutions
- Complete all addComparator/setComparator/setForwardSort/setReverseSort calls before the first compare() invocation.
- Create a new ComparatorChain instance if you need a different ordering after comparisons have started.
- Use the static factory methods (ComparatorChain.of(...)) to build a fully-configured chain in one step.
- Check chain.isLocked() before attempting modifications.
Example fix
// before
ComparatorChain<String> chain = new ComparatorChain<>(Comparator.naturalOrder());
Collections.sort(list, chain);
chain.addComparator(String.CASE_INSENSITIVE_ORDER); // throws — already locked
// after
ComparatorChain<String> chain = ComparatorChain.of(
Comparator.naturalOrder(), String.CASE_INSENSITIVE_ORDER
);
Collections.sort(list, chain); Defensive patterns
Strategy: validation
Validate before calling
if (!chain.isLocked()) {
chain.addComparator(newComparator);
} else {
// create a new chain instead
throw new IllegalStateException("ComparatorChain is locked");
} Prevention
- Configure all comparators before the first compare() call.
- Use ComparatorChain.of(...) factory methods for one-step construction.
- Check isLocked() before attempting modifications.
- Create a new instance rather than modifying a used one.
When it happens
Trigger: Calling chain.addComparator(...), chain.setComparator(...), chain.setForwardSort(...), or chain.setReverseSort(...) after chain.compare(a, b) has been called at least once. The lock flag is set to true on the first compare() invocation and checkLocked() is called by every mutator method.
Common situations: Reusing a ComparatorChain instance across multiple sort operations and trying to modify it between sorts. Building a chain lazily but starting to compare before construction is complete. Sharing a chain instance across threads where one thread compares while another modifies.
Related errors
- ComparatorChains must contain at least one Comparator
- Field [{}] not found in Class [{}]
- proxied annotation can not reset attributes
- This is not a mutable object !
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/4523c6c012d2eb4c.
Report an issue: GitHub.