下面列出了com.google.common.collect.Maps#difference ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
protected boolean doEquivalent(SolrInputField o1, SolrInputField o2) {
if (o1.getValue() instanceof SolrInputDocument) {
if (!(o2.getValue() instanceof SolrInputDocument)) {
return false;
}
final MapDifference<String, SolrInputField> difference = Maps.difference(
(SolrInputDocument) o1.getValue(),
(SolrInputDocument) o2.getValue(),
this
);
if (!difference.areEqual()) {
return false;
}
} else {
if (o1.getValue() != o2.getValue()) {
return false;
}
}
return true;
}
private CubeAssignment reassignCubeImpl(String cubeName, CubeAssignment preAssignments,
CubeAssignment newAssignments) {
logger.info("start cube reBalance, cube:{}, previous assignments:{}, new assignments:{}", cubeName,
preAssignments, newAssignments);
if (newAssignments.equals(preAssignments)) {
logger.info("the new assignment is the same as the previous assignment, do nothing for this reassignment");
return newAssignments;
}
CubeInstance cubeInstance = CubeManager.getInstance(KylinConfig.getInstanceFromEnv()).getCube(cubeName);
doReassign(cubeInstance, preAssignments, newAssignments);
MapDifference<Integer, List<Partition>> assignDiff = Maps.difference(preAssignments.getAssignments(),
newAssignments.getAssignments());
// add empty partitions to the removed replica sets, means that there's still data in the replica set, but no new data will be consumed.
Map<Integer, List<Partition>> removedAssign = assignDiff.entriesOnlyOnLeft();
for (Integer removedReplicaSet : removedAssign.keySet()) {
newAssignments.addAssignment(removedReplicaSet, Lists.<Partition> newArrayList());
}
streamMetadataStore.saveNewCubeAssignment(newAssignments);
AssignmentsCache.getInstance().clearCubeCache(cubeName);
return newAssignments;
}
@NotAtomicAndNotIdempotent
void reassignCubeImpl(String cubeName, CubeAssignment preAssignments, CubeAssignment newAssignments) {
logger.info("start cube reBalance, cube:{}, previous assignments:{}, new assignments:{}", cubeName,
preAssignments, newAssignments);
if (newAssignments.equals(preAssignments)) {
logger.info("the new assignment is the same as the previous assignment, do nothing for this reassignment");
return;
}
CubeInstance cubeInstance = getCoordinator().getCubeManager().getCube(cubeName);
doReassignWithoutCommit(cubeInstance, preAssignments, newAssignments);
// add empty partitions to the removed replica sets, means that there's still data in the replica set, but no new data will be consumed.
MapDifference<Integer, List<Partition>> assignDiff = Maps.difference(preAssignments.getAssignments(),
newAssignments.getAssignments());
Map<Integer, List<Partition>> removedAssign = assignDiff.entriesOnlyOnLeft();
for (Integer removedReplicaSet : removedAssign.keySet()) {
newAssignments.addAssignment(removedReplicaSet, Lists.<Partition> newArrayList());
}
logger.info("Commit reassign {} transaction.", cubeName);
getCoordinator().getStreamMetadataStore().saveNewCubeAssignment(newAssignments);
AssignmentsCache.getInstance().clearCubeCache(cubeName);
}
private static JobDiff computeUnscoped(
Map<Integer, ITaskConfig> currentState,
IJobKey job,
Map<Integer, ITaskConfig> proposedState) {
requireNonNull(job);
requireNonNull(proposedState);
MapDifference<Integer, ITaskConfig> diff = Maps.difference(currentState, proposedState);
Map<Integer, ITaskConfig> removedInstances = ImmutableMap.<Integer, ITaskConfig>builder()
.putAll(diff.entriesOnlyOnLeft())
.putAll(Maps.transformValues(diff.entriesDiffering(), JobDiff.leftValue()))
.build();
Set<Integer> addedInstances = ImmutableSet.<Integer>builder()
.addAll(diff.entriesOnlyOnRight().keySet())
.addAll(diff.entriesDiffering().keySet())
.build();
return new JobDiff(
removedInstances,
addedInstances,
ImmutableMap.copyOf(diff.entriesInCommon()));
}
@Override
public void syncMemberships(User user, Collection<String> groupNames) {
Map<String, Membership> syncMap = new HashMap<>();
for (String groupName: groupNames) {
Group group = groupManager.find(groupName);
if (group == null) {
logger.warn("Unable to find group: " + groupName);
} else {
Membership membership = new Membership();
membership.setGroup(group);
membership.setUser(user);
syncMap.put(groupName, membership);
}
}
Map<String, Membership> currentMap = new HashMap<>();
user.getMemberships().forEach(membership ->
currentMap.put(membership.getGroup().getName(), membership));
MapDifference<String, Membership> diff = Maps.difference(currentMap, syncMap);
diff.entriesOnlyOnLeft().values().forEach(membership -> delete(membership));
diff.entriesOnlyOnRight().values().forEach(membership -> save(membership));
}
static void validateRegions(Map<String, Collection<String>> regionsToAdd,
Map<String, Collection<String>> supportedRegions) {
MapDifference<String, Collection<String>>
comparison =
Maps.difference(regionsToAdd, supportedRegions);
checkArgument(comparison.entriesOnlyOnLeft().isEmpty(), "unsupported regions: %s", comparison
.entriesOnlyOnLeft().keySet());
for (Entry<String, Collection<String>> entry : regionsToAdd.entrySet()) {
ImmutableSet<String> toAdd = ImmutableSet.copyOf(entry.getValue());
SetView<String> intersection = Sets.intersection(toAdd,
ImmutableSet.copyOf(
supportedRegions.get(entry.getKey())));
SetView<String> unsupported = Sets.difference(toAdd, intersection);
checkArgument(unsupported.isEmpty(), "unsupported territories in %s:", entry.getKey(),
unsupported);
}
}
@Transactional
@Override
public void syncSshKeys(User user, Collection<String> sshKeys) {
Map<String, SshKey> syncMap = new HashMap<>();
for (String content: sshKeys) {
try {
PublicKey pubEntry = SshKeyUtils.decodeSshPublicKey(content);
String digest = KeyUtils.getFingerPrint(SshKey.DIGEST_FORMAT, pubEntry);
SshKey sshKey = new SshKey();
sshKey.setDigest(digest);
sshKey.setContent(content);
sshKey.setOwner(user);
sshKey.setDate(new Date());
syncMap.put(content, sshKey);
} catch (IOException | GeneralSecurityException e) {
logger.error("Error parsing SSH key", e);
}
}
Map<String, SshKey> currentMap = new HashMap<>();
user.getSshKeys().forEach(sshKey -> currentMap.put(sshKey.getContent(), sshKey));
MapDifference<String, SshKey> diff = Maps.difference(currentMap, syncMap);
diff.entriesOnlyOnLeft().values().forEach(sshKey -> delete(sshKey));
diff.entriesOnlyOnRight().values().forEach(sshKey -> {
if (findByDigest(sshKey.getDigest()) == null)
save(sshKey);
else
logger.warn("SSH key is already in use (digest: {})", sshKey.getDigest());
});
}
private static boolean roughlyEqual(String expectedRaw, String requestedPathRaw) {
LOG.debug("Comparing expected [{}] vs requested [{}]", expectedRaw, requestedPathRaw);
if (StringUtils.isEmpty(expectedRaw)) {
LOG.debug("False: empty expected");
return false;
}
try {
UriComponents expected = UriComponentsBuilder.fromUriString(expectedRaw).build();
UriComponents requested = UriComponentsBuilder.fromUriString(requestedPathRaw).build();
if (!Objects.equals(expected.getPath(), requested.getPath())) {
LOG.debug("False: expected path [{}] does not match requested path [{}]",
expected.getPath(), requested.getPath());
return false;
}
MapDifference<String, List<String>> difference = Maps.difference(expected.getQueryParams(),
requested.getQueryParams());
if (!difference.entriesDiffering().isEmpty() ||
!difference.entriesOnlyOnLeft().isEmpty() ||
difference.entriesOnlyOnRight().size() != 1 ||
difference.entriesOnlyOnRight().get(JWTSecurityService.JWT_PARAM_NAME) == null) {
LOG.debug("False: expected query params [{}] do not match requested query params [{}]", expected.getQueryParams(), requested.getQueryParams());
return false;
}
} catch (Exception e) {
LOG.warn("Exception encountered while comparing paths", e);
return false;
}
return true;
}
protected List<LeveledMessage> computePropertiesDiffMessages(
Supplier<LeveledMessage.Builder> supplier, Map<String, ConfigurationProperty> left, Map<String, ConfigurationProperty> right) {
final List<LeveledMessage> messages = new ArrayList<>();
final MapDifference<String, ConfigurationProperty> diff = Maps.difference(left, right);
for (Map.Entry<String, MapDifference.ValueDifference<ConfigurationProperty>> entry: diff.entriesDiffering().entrySet()) {
final MapDifference.ValueDifference<ConfigurationProperty> value = entry.getValue();
final ConfigurationProperty leftValue = value.leftValue();
final ConfigurationProperty rightValue = value.rightValue();
// Special handling because of dynamic metadata
if (!equals(leftValue, rightValue)) {
messages.add(
supplier.get()
.level(LeveledMessage.Level.INFO)
.code(LeveledMessage.Code.SYNDESIS001).build()
);
break;
}
}
if (!diff.entriesOnlyOnLeft().isEmpty() || !diff.entriesOnlyOnRight().isEmpty()) {
messages.add(
supplier.get()
.level(LeveledMessage.Level.WARN)
.code(LeveledMessage.Code.SYNDESIS002).build()
);
}
return messages;
}
public Map<String, String> getAvailableNamedIds(NameIdConfig config) {
MapDifference<String, String> diff = Maps.difference(availableNamedIds, usedNamedIds);
Map<String, String> value = diff.entriesOnlyOnLeft();
Map<String, String> result = Maps.newHashMap(value);
if (config.getNameIdType() != null) {
result.put(config.getNameIdType(), config.getNameIdType());
}
return result;
}
/**
* Updates the database object.
* @param databaseInfo database object
*/
public void update(final DatabaseInfo databaseInfo) {
log.debug("Start: Database update using direct sql for {}", databaseInfo.getName());
final long start = registry.clock().wallTime();
try {
final Long databaseId = getDatabaseId(databaseInfo.getName());
final DatabaseInfo existingDatabaseInfo = getDatabaseById(databaseId, databaseInfo.getName());
final Map<String, String> newMetadata = databaseInfo.getMetadata() == null ? Maps.newHashMap()
: databaseInfo.getMetadata();
final MapDifference<String, String> diff = Maps.difference(existingDatabaseInfo.getMetadata(), newMetadata);
insertDatabaseParams(databaseId, diff.entriesOnlyOnRight());
final Map<String, String> updateParams = diff.entriesDiffering().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, s -> s.getValue().rightValue()));
updateDatabaseParams(databaseId, updateParams);
final String uri =
Strings.isNullOrEmpty(databaseInfo.getUri()) ? existingDatabaseInfo.getUri() : databaseInfo.getUri();
final String newOwner = getOwner(databaseInfo.getAudit());
final String owner =
Strings.isNullOrEmpty(newOwner) ? newOwner : existingDatabaseInfo.getAudit().getCreatedBy();
jdbcTemplate.update(SQL.UPDATE_DATABASE, new SqlParameterValue(Types.VARCHAR, uri),
new SqlParameterValue(Types.VARCHAR, owner),
new SqlParameterValue(Types.BIGINT, databaseId));
} finally {
this.fastServiceMetric.recordTimer(
HiveMetrics.TagAlterDatabase.getMetricName(), registry.clock().wallTime() - start);
log.debug("End: Database update using direct sql for {}", databaseInfo.getName());
}
}
public static void assertMap(Map<String, ?> expected, Map<String, ?> actual, String message) {
if (null == expected && null == actual) {
return;
}
String prefix = Strings.isNullOrEmpty(message) ? "" : message + ": ";
assertNotNull(expected, prefix + "expected cannot be null");
assertNotNull(actual, prefix + "actual cannot be null");
MapDifference<String, ?> mapDifference = Maps.difference(expected, actual);
assertTrue(mapDifference.areEqual(), new MapDifferenceSupplier(mapDifference, prefix));
}
private static boolean roughlyEqual(String expectedRaw, String requestedPathRaw) {
LOG.debug("Comparing expected [{}] vs requested [{}]", expectedRaw, requestedPathRaw);
if (StringUtils.isEmpty(expectedRaw)) {
LOG.debug("False: empty expected");
return false;
}
try {
UriComponents expected = UriComponentsBuilder.fromUriString(expectedRaw).build();
UriComponents requested = UriComponentsBuilder.fromUriString(requestedPathRaw).build();
if (!Objects.equals(expected.getPath(), requested.getPath())) {
LOG.debug("False: expected path [{}] does not match requested path [{}]",
expected.getPath(), requested.getPath());
return false;
}
Map<String, List<String>> left = new HashMap<>(expected.getQueryParams());
Map<String, List<String>> right = new HashMap<>(requested.getQueryParams());
/*
If the equality test uses the size parameter on the request, it is possible that the equality test will fail because Sonos
changes the size parameter according to the client.
All parameters should be removed, but this would require too much retrofit work throughout the code.
*/
left.remove("size");
right.remove("size");
MapDifference<String, List<String>> difference = Maps.difference(left, right);
if (difference.entriesDiffering().isEmpty() || difference.entriesOnlyOnLeft().isEmpty()
|| (difference.entriesOnlyOnRight().size() == 1 && difference.entriesOnlyOnRight().get(JWTSecurityService.JWT_PARAM_NAME) != null)) {
return true;
}
LOG.debug("False: expected query params [{}] do not match requested query params [{}]", expected.getQueryParams(), requested.getQueryParams());
return false;
} catch (Exception e) {
LOG.warn("Exception encountered while comparing paths", e);
return false;
}
}
@Test
public void givenSimilarMapsWithArrayValue_whenCompareUsingGuava_thenFail() {
MapDifference<String, String[]> diff = Maps.difference(asiaCity1, asiaCity2);
assertFalse(diff.areEqual());
}
boolean isModified(Namespace namespace) {
Release release = releaseService.findLatestActiveRelease(namespace);
List<Item> items = itemService.findItemsWithoutOrdered(namespace.getId());
if (release == null) {
return hasNormalItems(items);
}
Map<String, String> releasedConfiguration = gson.fromJson(release.getConfigurations(), GsonType.CONFIG);
Map<String, String> configurationFromItems = generateConfigurationFromItems(namespace, items);
MapDifference<String, String> difference = Maps.difference(releasedConfiguration, configurationFromItems);
return !difference.areEqual();
}
/**
* Compares sets of config options, and returns the difference as a map of 'section.key' strings
* to pairs containing the different values.
*/
@VisibleForTesting
public static Map<String, ConfigChange> compare(
ImmutableMap<String, ImmutableMap<String, String>> rawConfig1,
ImmutableMap<String, ImmutableMap<String, String>> rawConfig2) {
MapDifference<String, ImmutableMap<String, String>> diffSections =
Maps.difference(rawConfig1, rawConfig2);
if (!diffSections.areEqual()) {
ImmutableMap.Builder<String, ConfigChange> result = ImmutableMap.builder();
BiConsumer<String, Map<String, ValueDifference<String>>> appendChange =
(section, diff) ->
diff.forEach(
(option, value) ->
result.put(
section + "." + option,
ImmutableConfigChange.of(value.leftValue(), value.rightValue())));
BiConsumer<String, Map<String, String>> appendLeft =
(section, diff) ->
diff.forEach(
(option, value) ->
result.put(section + "." + option, ImmutableConfigChange.of(value, null)));
BiConsumer<String, Map<String, String>> appendRight =
(section, diff) ->
diff.forEach(
(option, value) ->
result.put(section + "." + option, ImmutableConfigChange.of(null, value)));
diffSections
.entriesDiffering()
.forEach(
(section, diff) -> {
MapDifference<String, String> sectionDiff =
Maps.difference(diff.leftValue(), diff.rightValue());
appendChange.accept(section, sectionDiff.entriesDiffering());
appendLeft.accept(section, sectionDiff.entriesOnlyOnLeft());
appendRight.accept(section, sectionDiff.entriesOnlyOnRight());
});
diffSections.entriesOnlyOnLeft().forEach(appendLeft);
diffSections.entriesOnlyOnRight().forEach(appendRight);
return result.build();
}
return ImmutableMap.of();
}
@Test
public void testWriteFormatUnchanged() {
// Attempts to flag any changes in the storage format. While thorough, this check is not
// complete. It attempts to capture the entire schema by synthesizing a fully-populated
// instance of each Op type. For TUnions, the struct generator picks an arbitrary field to set,
// meaning that it will only see one of the multiple possible schemas for any given TUnion.
// These generated structs effectively give a view of the struct schema, which is compared to
// golden files in `goldens/current`.
Map<String, String> schemasByName = generateOpSchemas();
File goldensDir = getGoldensDir("current");
Map<String, String> goldensByName = loadGoldenSchemas(goldensDir);
MapDifference<String, String> difference = Maps.difference(goldensByName, schemasByName);
if (difference.areEqual()) {
return;
}
StringBuilder error = new StringBuilder();
StringBuilder remedy = new StringBuilder();
Set<String> removedOps = difference.entriesOnlyOnLeft().keySet();
if (!removedOps.isEmpty()) {
error.append("Removal of storage Op(s): ").append(removedOps)
.append("\nOps may only be removed after a release that")
.append("\n * formally deprecates the Op in release notes")
.append("\n * performs a no-op read of the Op type")
.append("\n * included warning logging when the Op was read")
.append("\n * ensures the Op is removed from storage")
.append("\n\nHowever, you should also consider leaving the Op indefinitely and removing")
.append("\nall fields as a safer alternative.");
remedy.append("deleting the files ")
.append(removedOps.stream()
.map(removed -> new File(goldensDir, removed).getAbsolutePath())
.collect(Collectors.joining(", ")));
}
String goldenChangeInstructions = Streams.concat(
difference.entriesOnlyOnRight().entrySet().stream(),
difference.entriesDiffering().entrySet().stream()
.map(entry ->
new SimpleImmutableEntry<>(entry.getKey(), entry.getValue().rightValue())))
.map(entry -> new StringBuilder()
.append("\n").append(new File(goldensDir, entry.getKey()).getPath()).append(":")
.append("\n").append(entry.getValue())
.toString())
.collect(Collectors.joining("\n"));
Set<String> addedOps = difference.entriesOnlyOnRight().keySet();
if (!addedOps.isEmpty()) {
error.append("Addition of storage Op(s): ").append(addedOps)
.append("\nOps may only be introduced")
.append("\n a.) in a release that supports reading but not writing the Op")
.append("\n b.) in a release that writes the Op only with an operator-controlled flag");
remedy.append("creating the following files")
.append(goldenChangeInstructions);
}
Map<String, ValueDifference<String>> modified = difference.entriesDiffering();
if (!modified.isEmpty()) {
error.append("Schema changes to Op(s): " + modified.keySet())
.append("\nThis check detects that changes occurred, not how the schema changed.")
.append("\nSome guidelines for evolving schemas:")
.append("\n * Introducing fields: you must handle reading records that do not")
.append("\n yet have the field set. This can be done with a backfill routine during")
.append("\n storage recovery if a field is required in some parts of the code")
.append("\n * Removing fields: must only be done after a release in which the field")
.append("\n is unused and announced as deprecated")
.append("\n * Changed fields: the type or thrift field ID of a field must never change");
remedy.append("changing the following files")
.append(goldenChangeInstructions);
}
fail(new StringBuilder()
.append("**** Storage compatibility change detected ****")
.append("\n")
.append(error)
.append("\n\nIf the necessary compatibility procedures have been performed,")
.append("\nyou may clear this check by ")
.append(remedy)
.toString());
}
/**
* Returns true if the passed parsing result's options have the same value as these options.
*
* <p>If a native parsed option is passed whose fragment has been trimmed in these options it is
* considered to match.
*
* <p>If no options are present in the parsing result or all options in the parsing result have
* been trimmed the result is considered not to match. This is because otherwise the parsing
* result would match any options in a similar trimmed state, regardless of contents.
*
* @param parsingResult parsing result to be compared to these options
* @return true if all non-trimmed values match
* @throws OptionsParsingException if options cannot be parsed
*/
public boolean matches(OptionsParsingResult parsingResult) throws OptionsParsingException {
Set<OptionDefinition> ignoredDefinitions = new HashSet<>();
for (ParsedOptionDescription parsedOption : parsingResult.asListOfExplicitOptions()) {
OptionDefinition optionDefinition = parsedOption.getOptionDefinition();
// All options obtained from an options parser are guaranteed to have been defined in an
// FragmentOptions class.
@SuppressWarnings("unchecked")
Class<? extends FragmentOptions> fragmentClass =
(Class<? extends FragmentOptions>) optionDefinition.getField().getDeclaringClass();
FragmentOptions originalFragment = fragmentOptionsMap.get(fragmentClass);
if (originalFragment == null) {
// Ignore flags set in trimmed fragments.
ignoredDefinitions.add(optionDefinition);
continue;
}
Object originalValue = originalFragment.asMap().get(optionDefinition.getOptionName());
if (!Objects.equals(originalValue, parsedOption.getConvertedValue())) {
return false;
}
}
Map<Label, Object> starlarkOptions =
labelizeStarlarkOptions(parsingResult.getStarlarkOptions());
MapDifference<Label, Object> starlarkDifference =
Maps.difference(starlarkOptionsMap, starlarkOptions);
if (starlarkDifference.entriesInCommon().size() < starlarkOptions.size()) {
return false;
}
if (ignoredDefinitions.size() == parsingResult.asListOfExplicitOptions().size()
&& starlarkOptions.isEmpty()) {
// Zero options were compared, either because none were passed or because all of them were
// trimmed.
return false;
}
return true;
}
public static void assertSolrInputDocument(SolrInputDocument expected, SolrInputDocument actual) {
assertNotNull(actual);
MapDifference<String, SolrInputField> difference = Maps.difference(expected, actual, new SolrInputFieldEquivalence());
assertTrue(difference.areEqual(), new MapDifferenceSupplier(difference));
}
/**
* 对两个Map进行比较,返回MapDifference,然后各种妙用.
*
* 包括key的差集,key的交集,以及key相同但value不同的元素。
*
* @see com.google.common.collect.MapDifference
*/
public static <K, V> MapDifference<K, V> difference(Map<? extends K, ? extends V> left,
Map<? extends K, ? extends V> right) {
return Maps.difference(left, right);
}