1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import java.util.HashMap;

public final class HeterogeneousHashMap<K> {

	private final HashMap<K, Class<?>> classes;
	private final HashMap<K, Object> values;

	public static <K> HeterogeneousHashMap<K> create() {
		return new HeterogeneousHashMap<K>();
	}
	
	public HeterogeneousHashMap() {
		classes = new HashMap<K, Class<?>>();
		values = new HashMap<K, Object>();
	}

	public <T> T get(K key) {
		assertSizesAreEqual();
		@SuppressWarnings("unchecked")
		Class<T> clasz = (Class<T>) classes.get(key);
		Object value = values.get(key);
		return clasz == null || value == null ? null : clasz.cast(value);
	}

	public <T> void put(K key, T value) {
		if (key == null)
			return;

		Class<?> clasz = getClassOrNull(value);

		classes.put(key, clasz);
		values.put(key, value);
	}

	private <T> Class<?> getClassOrNull(T value) {
		return value == null ? null : value.getClass();
	}

	public boolean contains(K key) {
		return classes.containsKey(key) && values.containsKey(key);
	}
	
	public int size() {
		assertSizesAreEqual();
		return classes.size();
	}

	private void assertSizesAreEqual() {
		assert classes.size() == values.size();
	}

	public void remove(K key) {
		assertSizesAreEqual();
		assertValuesArePresent(key);
		classes.remove(key);
		values.remove(key);
	}

	private void assertValuesArePresent(K key) {
		assert classes.containsKey(key);
		assert values.containsKey(key);
	}

	public char getChar(K key) {
		try {
			Character value = get(key);
			return value == null ? '0' : value;
		} catch (ClassCastException cce) {
			return '0';
		}
	}

	public byte getByte(K key) {
		try {
			Byte value = get(key);
			return value == null ? 0 : value;
		} catch (ClassCastException cce) {
			return 0;
		}
	}

	public short getShort(K key) {
		try {
			Short value = get(key);
			return value == null ? 0 : value;
		} catch (ClassCastException cce) {
			return 0;
		}
	}

	public int getInt(K key) {
		try {
			Integer value = get(key);
			return value == null ? 0 : value;
		} catch (ClassCastException cce) {
			return 0;
		}
	}

	public long getLong(K key) {
		try {
			Long value = get(key);
			return value == null ? 0 : value;
		} catch (ClassCastException cce) {
			return 0;
		}
	}

	public boolean getBoolean(K key) {
		try {
			Boolean value = get(key);
			return !(value == null);
		} catch (ClassCastException cce) {
			return false;
		}
	}

	public float getFloat(K key) {
		try {
			Float value = get(key);
			return value == null ? 0 : value;
		} catch (ClassCastException cce) {
			return 0;
		}
	}

	public double getDouble(K key) {
		try {
			Double value = get(key);
			return value == null ? 0 : value;
		} catch (ClassCastException cce) {
			return 0;
		}
	}

}