Skip to content

UseMapOf converts order-sensitive LinkedHashMap to Map.of, silently dropping iteration-order guarantee #1112

@protocol7

Description

@protocol7

What version of OpenRewrite are you using?

  • rewrite-migrate-java: 3.35.0
  • rewrite-core: 8.83.2
  • rewrite-java: 8.83.2

What is the smallest, simplest way to reproduce the problem?

import java.util.LinkedHashMap;
import java.util.Map;

class Main {
  static final Map<String, String> ORDERED =
      new LinkedHashMap<>() {
        {
          put("a", "1");
          put("b", "2");
          put("c", "3");
        }
      };
}

Run org.openrewrite.java.migrate.util.UseMapOf.

What did you expect to see?

No change. LinkedHashMap guarantees insertion-order iteration, whereas Map.of(...) makes no ordering guarantee at all (the iteration order of the immutable maps returned by Map.of is explicitly unspecified and is in fact deliberately randomized per JVM instance). Converting LinkedHashMap (or TreeMap) to Map.of silently drops the ordering contract that the original code relied on.

UseMapOf should only apply to map implementations whose iteration order is already unspecified (i.e. java.util.HashMap). It should skip order-sensitive implementations such as LinkedHashMap and TreeMap.

What did you see instead?

The recipe rewrites the order-preserving LinkedHashMap to Map.of, discarding the insertion-order guarantee:

import java.util.LinkedHashMap;
import java.util.Map;

class Main {
  static final Map<String, String> ORDERED =
          Map.of(
          "a", "1",
          "b", "2",
          "c", "3");
}

Any code iterating over ORDERED (e.g. serializing it to YAML/JSON, or driving an ordered rollout) now observes an arbitrary, run-to-run-varying order. This is a behavioral change, not a refactoring, and it compiles cleanly so it passes silently until runtime.

(The unused LinkedHashMap import is also left behind, but the ordering regression is the substantive problem.)

What is the full stack trace of any errors you encountered?

No error: the transformation produces compiling code with changed runtime semantics. The regression surfaces downstream as a failing test or incorrect ordering at runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions