public int uncommittedChanges() {
   Set<Map.Entry<String, Storeable>> indexedSet = tableIndexedData.entrySet();
   Set<Map.Entry<String, Storeable>> diskSet = tableOnDisk.entrySet();
   int count = 0;
   Iterator<Map.Entry<String, Storeable>> iter1 = indexedSet.iterator();
   Iterator<Map.Entry<String, Storeable>> iter2 = diskSet.iterator();
   while (iter1.hasNext()) {
     Map.Entry<String, Storeable> next = iter1.next();
     Storeable entryOnDisk = tableOnDisk.get(next.getKey());
     if (entryOnDisk == null) {
       // записи на диске нет, то она изменение
       ++count;
     } else if (!entryOnDisk.equals(next.getValue())) {
       // запись на диске есть, но она другая, тоже изменение
       ++count;
     }
   }
   while (iter2.hasNext()) {
     Map.Entry<String, Storeable> next = iter2.next();
     Storeable entryIndexed = tableIndexedData.get(next.getKey());
     if (entryIndexed == null) {
       // на диске есть, индексированной нет, изменение
       ++count;
     }
   }
   return count;
 }
 private void checkValue(Storeable value) throws ColumnFormatException {
   if (value == null) {
     throw new IllegalArgumentException("Value is null");
   }
   try {
     for (int i = 0; i < columnTypes.size(); ++i) {
       if (value.getColumnAt(i) != null
           && !columnTypes.get(i).equals(value.getColumnAt(i).getClass())) {
         throw new ColumnFormatException(
             "Wrong column type. was: "
                 + value.getColumnAt(i).getClass().toString()
                 + "; expected: "
                 + columnTypes.get(i));
       }
     }
     boolean unusedValue = true;
     try {
       value.getColumnAt(columnTypes.size());
     } catch (IndexOutOfBoundsException e) {
       unusedValue = false;
     }
     if (unusedValue) {
       throw new ColumnFormatException("Alien value");
     }
   } catch (IndexOutOfBoundsException e) {
     throw new ColumnFormatException("Alien value", e);
   }
 }
 /**
  * Устанавливает значение по указанному ключу.
  *
  * @param key Ключ для нового значения. Не может быть null.
  * @param value Новое значение. Не может быть null.
  * @return Значение, которое было записано по этому ключу ранее. Если ранее значения не было
  *     записано, возвращает null.
  * @throws IllegalArgumentException Если значение параметров key или value является null.
  * @throws ru.fizteh.fivt.storage.structured.ColumnFormatException - при попытке передать
  *     Storeable с колонками другого типа.
  */
 @Override
 public Storeable put(String key, Storeable value) throws ColumnFormatException {
   checkKey(key);
   checkValue(value);
   Storeable oldValue = tableIndexedData.get(key);
   if (oldValue == null || !oldValue.equals(value)) {
     HashcodeDestination dest = new HashcodeDestination(key);
     tableFileModified[dest.getDir()][dest.getFile()] = true;
     tableIndexedData.put(key, value);
   }
   return oldValue;
 }