我需要使用Key(自定义对象)和Value As Set自定义对象创建一个哈希代码,我使用Guava 18.0
@Getter
public final class StockKey {
@ValidIsin
private final String isin;
@ValidExchangeId
private final Integer exchangeId;
@ValidCurrency
private final String currency
}
@EqualsAndHashCode
public final class ClientAssetPosition {
public static final double EPSILON = 0.0001;
@NotNull
private final PositionType type;
@NotNull
private final Double quantity;
@Nullable
@Getter
private Double coveredOptions;
@Nullable
@Getter
private Double blockedCoveringUnderlyings;
@Getter
@Setter
private Boolean excluded;
}
所以我有一个创建HashCode的函数
public static HashCode getHashCodeWithSha256(Map<StockKey, Set<ClientAssetPosition>> positions) {
final Hasher hasher = Hashing.sha256().newHasher();
for (Map.Entry<StockKey, Set<ClientAssetPosition>> positionsEntry : positions.entrySet()) {
hasher.putObject(positionsEntry.getKey(), STOCK_KEY_FUNNEL);
for (ClientAssetPosition asset : positionsEntry.getValue()) {
hasher.putObject(asset, CLIENT_ASSET_POSITION_FUNNEL);
}
}
return hasher.hash();
}
我使用这样的漏斗
public static final Funnel<StockKey> STOCK_KEY_FUNNEL = new Funnel<StockKey>() {
@Override
public void funnel(StockKey from, PrimitiveSink into) {
into.putString(from.getIsin()).putString(from.getCurrency()).putInt(from.getExchangeId());
}
};
public static final Funnel<ClientAssetPosition> CLIENT_ASSET_POSITION_FUNNEL = new Funnel<ClientAssetPosition>() {
@Override
public void funnel(ClientAssetPosition from, PrimitiveSink into) {
into.putDouble(from.getQuantity()).putString(from.getType().name());
}
};
对于相同的Map,这个函数有时会返回不同的HashCode 我通过这个单元测试找到它。如果从maven运行它,则此测试失败,但不是每次都运行。
@Test
public void testSamePortfolioSameHAshCodeOrdersASC(){
Map<StockKey, Set<ClientAssetPosition>> positions = new HashMap<>();
positions.put(PredefinedStockKeys.UBS, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBS_FEB_12_17_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBSN_MAR12_12_5_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_M10));
positions.put(PredefinedStockKeys.UBSN_MAR12_12_5_P, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBSN_MAR12_13_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBSN_MAY12_13_C, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_M10));
positions.put(PredefinedStockKeys.UBSN_MAR12_13_P, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBS_JAN_12_17_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.C_ORD_M1));
positions.put(PredefinedStockKeys.UBS_JAN_12_17_P, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBSH_APR12, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBSH_MAR12, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_B101));
positions.put(PredefinedStockKeys.UBSH_MAY12, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_B101));
Map<StockKey, Set<ClientAssetPosition>> positionsv2 = new HashMap<>();
positionsv2.put(PredefinedStockKeys.UBS, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBSN_MAR12_12_5_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_M10));
positionsv2.put(PredefinedStockKeys.UBSN_MAR12_12_5_P, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBSH_APR12, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBSN_MAY12_13_C, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_M10));
positionsv2.put(PredefinedStockKeys.UBSN_MAR12_13_P, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBS_JAN_12_17_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.C_ORD_M1));
positionsv2.put(PredefinedStockKeys.UBS_JAN_12_17_P, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBS_FEB_12_17_C, Sets.newHashSet(PredefinedAssetPosition.OWN_1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBSH_MAR12, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_B101));
positionsv2.put(PredefinedStockKeys.UBSN_MAR12_13_C, Sets.newHashSet(PredefinedAssetPosition.ORD_B101, PredefinedAssetPosition.OWN_1));
positionsv2.put(PredefinedStockKeys.UBSH_MAY12, Sets.newHashSet(PredefinedAssetPosition.C_ORD_M1, PredefinedAssetPosition.ORD_B101));
HashCode hashCodeWithSha256Expected = HashHelper.getHashCodeWithSha256(positions);
HashCode hashCodeWithSha256Exist = HashHelper.getHashCodeWithSha256(positionsv2);
Assert.assertArrayEquals(hashCodeWithSha256Expected.asBytes(), hashCodeWithSha256Exist.asBytes());
}
有人能解释我的错误吗?
答案 0 :(得分:3)
我认为问题与订购有关。即使从一次调用到另一次调用,如果分别在HashMap
或HashSet
中放置相同的键/值对或值,也无法保证两个调用之间的条目顺序保持不变。当然,JVM运行的情况要差得多。
因此,您需要重写哈希计算方法,以便在计算哈希值之前强制执行顺序...
其中,因为您使用Guava,很容易:使用Ordering.sortedCopy()
。
应该这样做;但是,请注意,这假设您的StockKey
和ClientAssetPosition
类实施Comparable
:
public static HashCode getHashCodeWithSha256(Map<StockKey, Set<ClientAssetPosition>> positions)
{
final Hasher hasher = Hashing.sha256().newHasher();
final Iterable<StockKey> orderedKeys
= Ordering.sortedCopy(positions.keySet());
Iterable<ClientAssetPosition> orderedAssets;
for (final StockKey key: orderedKeys) {
hasher.putObject(key, STOCK_KEY_FUNNEL);
orderedAssets = Ordering.sortedCopy(positions.get(key));
for (final ClientAssetPosition asset: orderedAssets)
hasher.putObject(asset, CLIENT_ASSET_POSITION_FUNNEL);
}
return hasher.hash();
}
HOWEVER :真的考虑切换到Multimap
。并且回想一下,如果您在某些时候需要向后兼容性,它也有.asMap()
方法。
答案 1 :(得分:1)
如果您查看Hasher documentation,您会发现:
警告:放入Hasher的数据块不是 分隔。生成的HashCode仅依赖于字节 插入,以及它们的插入顺序......
顺序问题,具有不同键/值顺序的哈希映射会产生不同的哈希值。
您可以通过在getHashCodeWithSha256中使用有序/排序结构或散列预处理来解决此问题。