Android ゲーム用のガベージ コレクターに適した文字列キャッシュを開発しました。その目的は、int の文字列を処理することです。私はそれを実装するというばかげた間違いを犯しましたが、バグがデスクトップで明らかになることはありませんでした。しかし、Android では、キャッシュがすぐに変な文字列を返し始めました。
class IntStringCache {
private final Map<IntStringCache.IntCacheKey, String> cachedStrings = new HashMap<IntStringCache.IntCacheKey, String>();
private final IntCacheKey tempIntCacheKey = new IntCacheKey(0);
public String getStringFor(int i) {
tempIntCacheKey.setIntValue(i);
String stringValue = cachedStrings.get(tempIntCacheKey);
if (stringValue == null) {
stringValue = String.valueOf(i);
// ERROR - putting the same object instead of new IntCachKey(i)
cachedStrings.put(tempIntCacheKey, stringValue);
}
return stringValue;
}
public int getSize() {
return cachedStrings.size();
}
private class IntCacheKey {
private int intValue;
private IntCacheKey(int intValue) {
this.intValue = intValue;
}
private void setIntValue(int intValue) {
this.intValue = intValue;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + intValue;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IntCacheKey other = (IntCacheKey) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (intValue != other.intValue)
return false;
return true;
}
private IntStringCache getOuterType() {
return IntStringCache.this;
}
}
そして、すべて合格するテスト:
public class IntStringCacheTest {
private IntStringCache intStringCache = new IntStringCache();
@Test
public void shouldCacheString() {
// given
int i = 1;
// when
String s1 = intStringCache.getStringFor(i);
String s2 = intStringCache.getStringFor(i);
// then
assertThat(s1).isNotNull();
assertThat(s1).isEqualTo(String.valueOf(i));
assertThat(s1).isSameAs(s2);
}
@Test
public void shouldCacheTwoValues() {
// given
int i1 = 1;
int i2 = 2;
int expectedCacheSize = 2;
// when
String s1 = intStringCache.getStringFor(i1);
String s2 = intStringCache.getStringFor(i2);
// then
assertThat(intStringCache.getSize()).isEqualTo(expectedCacheSize);
assertThat(s1).isSameAs(intStringCache.getStringFor(i1));
assertThat(s2).isSameAs(intStringCache.getStringFor(i2));
}
}
ノート:
assertThat(String.valueOf(1)).isSameAs(String.valueOf(1));
失敗します。
2 番目のテストに合格したという事実は興味深いものです。バグがあると、マップ内に更新されるキーが 1 つあるはずだからです。これは、HashMap 内の 2 つの異なるバケットに同じキーを入れる可能性のある hashCode() で説明できます。しかし、同じキーが (たとえ 2 つのバケットであっても) 同じ 2 つのスティングを返す可能性があるのはなぜでしょうか? コードにバグがあっても、HashMap は正しく機能しているようです。
一方、私の Android Java 実装は、このバグで一度に間違った数値の文字列を返します。