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", null);
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", "examplestreet");
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
) { Set
<Entry
<String,
?>> leftEntriesWithNonNullValues
= extractEntriesWithNonNullValues(left);
Set
<Entry
<String,
?>> rightEntriesWithNonNullValues
= extractEntriesWithNonNullValues(right);
Set<String> leftKeysWithNonNullValues = leftEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
Set<String> rightKeysWithNonNullValues = rightEntriesWithNonNullValues.stream()
.map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
if (!Objects.equals(leftKeysWithNonNullValues, rightKeysWithNonNullValues)) {
return false;
}
Set<String> leftKeysWithMapValues =
extractAllKeysThatMapToMaps(leftEntriesWithNonNullValues);
Set<String> rightKeysWithMapValues =
extractAllKeysThatMapToMaps(rightEntriesWithNonNullValues);
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
(Set
<Entry
<String,
?>> entrySet
) { return entrySet.stream()
.
filter(e
-> e.
getValue() instanceof Map) .map(Entry::getKey)
.collect(Collectors.toUnmodifiableSet());
}
private static Set
<Entry
<String,
?>> extractEntriesWithNonNullValues
(Map
<String,
?> map
) { return map.entrySet().stream()
.filter(e -> Objects.nonNull(e.getValue()))
.collect(Collectors.toUnmodifiableSet());
}
}
aW1wb3J0IGphdmEudXRpbC5IYXNoTWFwOwppbXBvcnQgamF2YS51dGlsLk1hcDsKaW1wb3J0IGphdmEudXRpbC5NYXAuRW50cnk7CmltcG9ydCBqYXZhLnV0aWwuT2JqZWN0czsKaW1wb3J0IGphdmEudXRpbC5TZXQ7CmltcG9ydCBqYXZhLnV0aWwuc3RyZWFtLkNvbGxlY3RvcnM7CgpjbGFzcyBJZGVvbmUgewogICAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYXJncykgewogICAgICAgIE1hcDxTdHJpbmcsIE9iamVjdD4gam9obkRvZSA9IG5ldyBIYXNoTWFwPD4oKTsKICAgICAgICBqb2huRG9lLnB1dCgibmFtZSIsICJKb2huIERvZSIpOwogICAgICAgIE1hcDxTdHJpbmcsIE9iamVjdD4gam9obkRvZXNBZGRyZXNzID0gbmV3IEhhc2hNYXA8PigpOwogICAgICAgIGpvaG5Eb2VzQWRkcmVzcy5wdXQoInN0cmVldCIsICJzYW1wbGVzdHJlc3MiKTsKICAgICAgICBqb2huRG9lc0FkZHJlc3MucHV0KCJjaXR5IiwgbnVsbCk7CiAgICAgICAgam9obkRvZXNBZGRyZXNzLnB1dCgiY291bnRyeSIsICJzYW1wbGVjb3VudHJ5Iik7CiAgICAgICAgam9obkRvZS5wdXQoImFkZHJlc3MiLCBqb2huRG9lc0FkZHJlc3MpOwoKICAgICAgICBNYXA8U3RyaW5nLCBPYmplY3Q+IGphbmVEb2UgPSBuZXcgSGFzaE1hcDw+KCk7CiAgICAgICAgamFuZURvZS5wdXQoIm5hbWUiLCAiSmFuZSIpOwogICAgICAgIE1hcDxTdHJpbmcsIE9iamVjdD4gamFuZURvZXNBZGRyZXNzID0gbmV3IEhhc2hNYXA8PigpOwogICAgICAgIGphbmVEb2VzQWRkcmVzcy5wdXQoInN0cmVldCIsICJleGFtcGxlc3RyZWV0Iik7CiAgICAgICAgamFuZURvZXNBZGRyZXNzLnB1dCgiY291bnRyeSIsICJleGFtcGxlY291bnRyeSIpOwogICAgICAgIGphbmVEb2UucHV0KCJhZGRyZXNzIiwgamFuZURvZXNBZGRyZXNzKTsKCiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKGFyZVN0cnVjdHVyYWxFcXVhbChqb2huRG9lLCBqYW5lRG9lKSk7CiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKGFyZVN0cnVjdHVyYWxFcXVhbChqb2huRG9lc0FkZHJlc3MsIGphbmVEb2UpKTsKICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oYXJlU3RydWN0dXJhbEVxdWFsKGpvaG5Eb2UsIGphbmVEb2VzQWRkcmVzcykpOwogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbihhcmVTdHJ1Y3R1cmFsRXF1YWwoam9obkRvZXNBZGRyZXNzLCBqYW5lRG9lc0FkZHJlc3MpKTsKICAgIH0KCiAgICBAU3VwcHJlc3NXYXJuaW5ncygidW5oZWNrZWQiKQogICAgcHVibGljIHN0YXRpYyBib29sZWFuIGFyZVN0cnVjdHVyYWxFcXVhbChNYXA8U3RyaW5nLCA/PiBsZWZ0LCBNYXA8U3RyaW5nLCA/PiByaWdodCkgewogICAgICAgIFNldDxFbnRyeTxTdHJpbmcsID8+PiBsZWZ0RW50cmllc1dpdGhOb25OdWxsVmFsdWVzID0gCiAgICAgICAgICAgICAgICBleHRyYWN0RW50cmllc1dpdGhOb25OdWxsVmFsdWVzKGxlZnQpOwogICAgICAgIFNldDxFbnRyeTxTdHJpbmcsID8+PiByaWdodEVudHJpZXNXaXRoTm9uTnVsbFZhbHVlcyA9CiAgICAgICAgICAgICAgICBleHRyYWN0RW50cmllc1dpdGhOb25OdWxsVmFsdWVzKHJpZ2h0KTsKICAgICAgICBTZXQ8U3RyaW5nPiBsZWZ0S2V5c1dpdGhOb25OdWxsVmFsdWVzID0gbGVmdEVudHJpZXNXaXRoTm9uTnVsbFZhbHVlcy5zdHJlYW0oKQogICAgICAgICAgICAgICAgLm1hcChFbnRyeTo6Z2V0S2V5KQogICAgICAgICAgICAgICAgLmNvbGxlY3QoQ29sbGVjdG9ycy50b1VubW9kaWZpYWJsZVNldCgpKTsKICAgICAgICBTZXQ8U3RyaW5nPiByaWdodEtleXNXaXRoTm9uTnVsbFZhbHVlcyA9IHJpZ2h0RW50cmllc1dpdGhOb25OdWxsVmFsdWVzLnN0cmVhbSgpCiAgICAgICAgICAgICAgICAubWFwKEVudHJ5OjpnZXRLZXkpCiAgICAgICAgICAgICAgICAuY29sbGVjdChDb2xsZWN0b3JzLnRvVW5tb2RpZmlhYmxlU2V0KCkpOwogICAgICAgIGlmICghT2JqZWN0cy5lcXVhbHMobGVmdEtleXNXaXRoTm9uTnVsbFZhbHVlcywgcmlnaHRLZXlzV2l0aE5vbk51bGxWYWx1ZXMpKSB7CiAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICB9CgogICAgICAgIFNldDxTdHJpbmc+IGxlZnRLZXlzV2l0aE1hcFZhbHVlcyA9CiAgICAgICAgICAgICAgICBleHRyYWN0QWxsS2V5c1RoYXRNYXBUb01hcHMobGVmdEVudHJpZXNXaXRoTm9uTnVsbFZhbHVlcyk7CiAgICAgICAgU2V0PFN0cmluZz4gcmlnaHRLZXlzV2l0aE1hcFZhbHVlcyA9CiAgICAgICAgICAgICAgICBleHRyYWN0QWxsS2V5c1RoYXRNYXBUb01hcHMocmlnaHRFbnRyaWVzV2l0aE5vbk51bGxWYWx1ZXMpOwogICAgICAgIGlmICghT2JqZWN0cy5lcXVhbHMobGVmdEtleXNXaXRoTWFwVmFsdWVzLCByaWdodEtleXNXaXRoTWFwVmFsdWVzKSkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBmb3IgKFN0cmluZyBrZXkgOiBsZWZ0S2V5c1dpdGhNYXBWYWx1ZXMpIHsKICAgICAgICAgICAgaWYgKCFhcmVTdHJ1Y3R1cmFsRXF1YWwoCiAgICAgICAgICAgICAgICAgICAgKE1hcDxTdHJpbmcsID8+KSBsZWZ0LmdldChrZXkpLAogICAgICAgICAgICAgICAgICAgIChNYXA8U3RyaW5nLCA/PikgcmlnaHQuZ2V0KGtleSkpKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgcHJpdmF0ZSBzdGF0aWMgU2V0PFN0cmluZz4gZXh0cmFjdEFsbEtleXNUaGF0TWFwVG9NYXBzKFNldDxFbnRyeTxTdHJpbmcsID8+PiBlbnRyeVNldCkgewogICAgICAgIHJldHVybiBlbnRyeVNldC5zdHJlYW0oKQogICAgICAgICAgICAgICAgLmZpbHRlcihlIC0+IGUuZ2V0VmFsdWUoKSBpbnN0YW5jZW9mIE1hcCkKICAgICAgICAgICAgICAgIC5tYXAoRW50cnk6OmdldEtleSkKICAgICAgICAgICAgICAgIC5jb2xsZWN0KENvbGxlY3RvcnMudG9Vbm1vZGlmaWFibGVTZXQoKSk7CiAgICB9CgogICAgcHJpdmF0ZSBzdGF0aWMgU2V0PEVudHJ5PFN0cmluZywgPz4+IGV4dHJhY3RFbnRyaWVzV2l0aE5vbk51bGxWYWx1ZXMoTWFwPFN0cmluZywgPz4gbWFwKSB7CiAgICAgICAgcmV0dXJuIG1hcC5lbnRyeVNldCgpLnN0cmVhbSgpCiAgICAgICAgICAgICAgICAuZmlsdGVyKGUgLT4gT2JqZWN0cy5ub25OdWxsKGUuZ2V0VmFsdWUoKSkpCiAgICAgICAgICAgICAgICAuY29sbGVjdChDb2xsZWN0b3JzLnRvVW5tb2RpZmlhYmxlU2V0KCkpOwogICAgfQp9