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

  1. Complete all addComparator/setComparator/setForwardSort/setReverseSort calls before the first compare() invocation.
  2. Create a new ComparatorChain instance if you need a different ordering after comparisons have started.
  3. Use the static factory methods (ComparatorChain.of(...)) to build a fully-configured chain in one step.
  4. 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

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


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/4523c6c012d2eb4c. Report an issue: GitHub.