import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
class Ideone {
public static void main
(String[] args
) { Map
<String, Object
> johnDoe
= new HashMap
<>(); johnDoe.put("name", "John Doe");
Map
<String, Object
> johnDoesAddress
= new HashMap
<>(); johnDoesAddress.put("street", "samplestress");
johnDoesAddress.put("city", "samplecity");
johnDoesAddress.put("country", "samplecountry");
johnDoe.put("address", johnDoesAddress);
Map
<String, Object
> janeDoe
= new HashMap
<>(); janeDoe.put("name", "Jane");
Map
<String, Object
> janeDoesAddress
= new HashMap
<>(); janeDoesAddress.put("street", null);
janeDoesAddress.put("city", "examplecity");
janeDoesAddress.put("country", "examplecountry");
janeDoe.put("address", janeDoesAddress);
System.
out.
println(areStructuralEqual
(johnDoe, janeDoe
)); System.
out.
println(areStructuralEqual
(johnDoesAddress, janeDoe
)); System.
out.
println(areStructuralEqual
(johnDoe, janeDoesAddress
)); System.
out.
println(areStructuralEqual
(johnDoesAddress, janeDoesAddress
)); }
@SuppressWarnings("unhecked")
public static boolean areStructuralEqual
(Map
<String,
?> left, Map
<String,
?> right
) { if (!Objects.equals(left.keySet(), right.keySet())) {
return false;
}
Set<String> leftKeysWithMapValues = extractAllKeysThatMapToMaps(left);
Set<String> rightKeysWithMapValues = extractAllKeysThatMapToMaps(right);
if (!Objects.equals(leftKeysWithMapValues, rightKeysWithMapValues)) {
return false;
}
for (String key
: leftKeysWithMapValues
) { if (!areStructuralEqual(
(Map
<String,
?>) left.
get(key
),
(Map
<String,
?>) right.
get(key
))) { return false;
}
}
return true;
}
private static Set
<String
> extractAllKeysThatMapToMaps
(Map
<String,
?> map
) { return map.entrySet().stream()
.
filter(e
-> e.
getValue() instanceof Map) .map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
}
aW1wb3J0IGphdmEudXRpbC5IYXNoTWFwOwppbXBvcnQgamF2YS51dGlsLk1hcDsKaW1wb3J0IGphdmEudXRpbC5NYXAuRW50cnk7CmltcG9ydCBqYXZhLnV0aWwuT2JqZWN0czsKaW1wb3J0IGphdmEudXRpbC5TZXQ7CmltcG9ydCBqYXZhLnV0aWwuc3RyZWFtLkNvbGxlY3RvcnM7CgpjbGFzcyBJZGVvbmUgewogICAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYXJncykgewogICAgICAgIE1hcDxTdHJpbmcsIE9iamVjdD4gam9obkRvZSA9IG5ldyBIYXNoTWFwPD4oKTsKICAgICAgICBqb2huRG9lLnB1dCgibmFtZSIsICJKb2huIERvZSIpOwogICAgICAgIE1hcDxTdHJpbmcsIE9iamVjdD4gam9obkRvZXNBZGRyZXNzID0gbmV3IEhhc2hNYXA8PigpOwogICAgICAgIGpvaG5Eb2VzQWRkcmVzcy5wdXQoInN0cmVldCIsICJzYW1wbGVzdHJlc3MiKTsKICAgICAgICBqb2huRG9lc0FkZHJlc3MucHV0KCJjaXR5IiwgInNhbXBsZWNpdHkiKTsKICAgICAgICBqb2huRG9lc0FkZHJlc3MucHV0KCJjb3VudHJ5IiwgInNhbXBsZWNvdW50cnkiKTsKICAgICAgICBqb2huRG9lLnB1dCgiYWRkcmVzcyIsIGpvaG5Eb2VzQWRkcmVzcyk7CgogICAgICAgIE1hcDxTdHJpbmcsIE9iamVjdD4gamFuZURvZSA9IG5ldyBIYXNoTWFwPD4oKTsKICAgICAgICBqYW5lRG9lLnB1dCgibmFtZSIsICJKYW5lIik7CiAgICAgICAgTWFwPFN0cmluZywgT2JqZWN0PiBqYW5lRG9lc0FkZHJlc3MgPSBuZXcgSGFzaE1hcDw+KCk7CiAgICAgICAgamFuZURvZXNBZGRyZXNzLnB1dCgic3RyZWV0IiwgbnVsbCk7CiAgICAgICAgamFuZURvZXNBZGRyZXNzLnB1dCgiY2l0eSIsICJleGFtcGxlY2l0eSIpOwogICAgICAgIGphbmVEb2VzQWRkcmVzcy5wdXQoImNvdW50cnkiLCAiZXhhbXBsZWNvdW50cnkiKTsKICAgICAgICBqYW5lRG9lLnB1dCgiYWRkcmVzcyIsIGphbmVEb2VzQWRkcmVzcyk7CgogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbihhcmVTdHJ1Y3R1cmFsRXF1YWwoam9obkRvZSwgamFuZURvZSkpOwogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbihhcmVTdHJ1Y3R1cmFsRXF1YWwoam9obkRvZXNBZGRyZXNzLCBqYW5lRG9lKSk7CiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKGFyZVN0cnVjdHVyYWxFcXVhbChqb2huRG9lLCBqYW5lRG9lc0FkZHJlc3MpKTsKICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oYXJlU3RydWN0dXJhbEVxdWFsKGpvaG5Eb2VzQWRkcmVzcywgamFuZURvZXNBZGRyZXNzKSk7CiAgICB9CgogICAgQFN1cHByZXNzV2FybmluZ3MoInVuaGVja2VkIikKICAgIHB1YmxpYyBzdGF0aWMgYm9vbGVhbiBhcmVTdHJ1Y3R1cmFsRXF1YWwoTWFwPFN0cmluZywgPz4gbGVmdCwgTWFwPFN0cmluZywgPz4gcmlnaHQpIHsKICAgICAgICBpZiAoIU9iamVjdHMuZXF1YWxzKGxlZnQua2V5U2V0KCksIHJpZ2h0LmtleVNldCgpKSkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBTZXQ8U3RyaW5nPiBsZWZ0S2V5c1dpdGhNYXBWYWx1ZXMgPSBleHRyYWN0QWxsS2V5c1RoYXRNYXBUb01hcHMobGVmdCk7CiAgICAgICAgU2V0PFN0cmluZz4gcmlnaHRLZXlzV2l0aE1hcFZhbHVlcyA9IGV4dHJhY3RBbGxLZXlzVGhhdE1hcFRvTWFwcyhyaWdodCk7CiAgICAgICAgaWYgKCFPYmplY3RzLmVxdWFscyhsZWZ0S2V5c1dpdGhNYXBWYWx1ZXMsIHJpZ2h0S2V5c1dpdGhNYXBWYWx1ZXMpKSB7CiAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICB9CgogICAgICAgIGZvciAoU3RyaW5nIGtleSA6IGxlZnRLZXlzV2l0aE1hcFZhbHVlcykgewogICAgICAgICAgICBpZiAoIWFyZVN0cnVjdHVyYWxFcXVhbCgKICAgICAgICAgICAgICAgICAgICAoTWFwPFN0cmluZywgPz4pIGxlZnQuZ2V0KGtleSksCiAgICAgICAgICAgICAgICAgICAgKE1hcDxTdHJpbmcsID8+KSByaWdodC5nZXQoa2V5KSkpIHsKICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBwcml2YXRlIHN0YXRpYyBTZXQ8U3RyaW5nPiBleHRyYWN0QWxsS2V5c1RoYXRNYXBUb01hcHMoTWFwPFN0cmluZywgPz4gbWFwKSB7CiAgICAgICAgcmV0dXJuIG1hcC5lbnRyeVNldCgpLnN0cmVhbSgpCiAgICAgICAgICAgICAgICAuZmlsdGVyKGUgLT4gZS5nZXRWYWx1ZSgpIGluc3RhbmNlb2YgTWFwKQogICAgICAgICAgICAgICAgLm1hcChFbnRyeTo6Z2V0S2V5KQogICAgICAgICAgICAgICAgLmNvbGxlY3QoQ29sbGVjdG9ycy50b1VubW9kaWZpYWJsZVNldCgpKTsKICAgIH0KfQ==