sclass FileBasedStringMap extends AbstractMap { File dir; bool compress; // use .gz *() {} *(File *dir) {} public S get(O a) { File f = fileForKey(a); ret dropFirstLine(compress ? loadGZTextFile(f) : loadTextFile(f)); } public synchronized Set> entrySet() { Set> set = new HashSet; for (File f : listFiles(dir)) { S md5 = md5FromUniqueFile(f); if (md5 == null) continue; fS key = unquote(compress ? firstLineFromGZTextFile(f) : firstLineFromFile(f)); set.add(new Map.Entry() { public S getKey() { ret key; } public S getValue() { ret get(key); } public S setValue(S s) { S old = get(key); put(key, s); ret old; } public bool equals(O o) { if (!(o instanceof Map.Entry)) false; Map.Entry e = (Map.Entry) o; ret eq(getKey(), e.getKey()) && eq(getValue(), e.getValue()); } public int hashCode() { S s = getValue(); ret (getKey()==null ? 0 : getKey().hashCode()) ^ (s == null ? 0 : s.hashCode()); } }); } ret set; } public S put(S a, S b) { S old = get(a); S text = b == null ? null : quote(a) + "\n" + b; if (compress) saveGZTextFile(fileForKey(a), text); else saveTextFile(fileForKey(a), text); ret old; } public S remove(O a) { if (!isString(a)) null; ret put((S) a, null); } File fileForKey(O key) { if (!isString(key)) null; ret new File(assertNotNull(dir), uniqueFileNameUsingMD5_80((S) key) + (compress ? ".gz" : ".txt")); } }