2011-01-27 4 views
3

Как я могу переопределить метод removeEldestEntry для сохранения старшей записи в файл? Также как ограничить размер файла, как я сделал это в LinkedHashMap. Вот код:removeEldestEntry overriding

import java.util.*; 

public class level1 { 
private static final int max_cache = 50; 
private Map cache = new LinkedHashMap(max_cache, .75F, true) { 
protected boolean removeEldestEntry(Map.Entry eldest) { 
    return size() > max_cache; 
} 
}; 


public level1() { 
for (int i = 1; i < 52; i++) { 
    String string = String.valueOf(i); 
    cache.put(string, string); 
    System.out.println("\rCache size = " + cache.size() + 
         "\tRecent value = " + i + " \tLast value = " + 
         cache.get(string) + "\tValues in cache=" + 
         cache.values()); 

} 

Я пытался использовать FileOutputStream:

private Map cache = new LinkedHashMap(max_cache, .75F, true) { 
    protected boolean removeEldestEntry(Map.Entry eldest) throws IOException { 
     boolean removed = super.removeEldestEntry(eldest); 
     if (removed) { 
      FileOutputStream fos = new FileOutputStream("t.tmp"); 
      ObjectOutputStream oos = new ObjectOutputStream(fos); 

      oos.writeObject(eldest.getValue()); 

      oos.close(); 
     } 
     return removed; 
    } 

Но я получил сообщение об ошибке

Ошибка (15,27): removeEldestEntry (java.util.Map .Entry) in не может переопределить removeEldestEntry (java.util.Map.Entry) в java.util.LinkedHashMap; переопределенный метод не бросает java.io.IOException

Без компилятора IOExecptio запрашивает обработку IOexception и Filenotfoundexception. Возможно, существует другой способ? Pls показывает мне пример кода, я новичок в java и просто пытаюсь понять основные принципы кэширования уровня 2. Thx

+0

BTW super.removeEldestEntry (старший) всегда возвращает ложь. Вы должны переопределить его, чтобы вернуть true, если хотите, чтобы старшая запись была сброшена. В вашем файле t.tmp будет сохранена только последняя запись. Это то, что вы намеревались? –

ответ

3

Сначала необходимо убедиться, что ваш метод правильно переопределяет родителя. Вы можете внести небольшие изменения в подпись, например, только бросать более конкретное проверенное исключение, которое является подклассом проверяемого исключения, объявленного в родительском элементе. В этом случае родительский элемент не объявляет ни одно проверенное исключение, поэтому вы не можете уточнить это и не можете делать какие-либо проверенные исключения. Поэтому вам придется обрабатывать IOException локально. Есть несколько способов сделать это, конвертировать его в RuntimeException и вносить в него.

Если вас беспокоит размер файла, вы, вероятно, не хотите хранить только последнюю удаленную запись, но многие из них - так что вы должны открыть файл для добавления.

Вам необходимо вернуть true из метода, чтобы фактически удалить старшего, и вам нужно решить, должен ли элемент быть удален.

При работе с файлами следует использовать try/finally, чтобы гарантировать, что вы закроете ресурс, даже если есть исключение. Это может стать немного уродливым - иногда приятно иметь метод утилиты для закрытия, чтобы вам не понадобилось дополнительное try/catch.

Как правило, вы также должны использовать некоторую буферизацию для ввода-вывода файлов, что значительно повышает производительность; в этом случае используйте обертку потока файлов в java.io.BufferedOutputStream и укажите это на ObjectOutputStream.

Вот то, что может делать то, что вы хотите:

private static final int MAX_ENTRIES_ALLOWED = 100; 
private static final long MAX_FILE_SIZE = 1L * 1024 * 1024; // 1 MB 

protected boolean removeEldestEntry(Map.Entry eldest) { 
    if (size() <= MAX_ENTRIES_ALLOWED) { 
     return false; 
    } 

    File objFile = new File("t.tmp"); 
    if (objFile.length() > MAX_FILE_SIZE) { 
     // Do something here to manage the file size, such as renaming the file 
     // You won't be able to easily remove an object from the file without a more 
     // advanced file structure since you are writing arbitrary sized serialized 
     // objects. You would need to do some kind of tagging of each entry or include 
     // a record length before each one. Then you would have to scan and rebuild 
     // a new file. You cannot easily just delete bytes earlier in the file without 
     // even more advanced structures (like having an index, fixed size records and 
     // free space lists, or even a database). 
    } 

    FileOutputStream fos = null; 
    try { 
     fos = new FileOutputStream(objFile, true); // Open for append 
     ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos)); 

     oos.writeObject(eldest.getValue()); 
     oos.close(); // Close the object stream to flush remaining generated data (if any). 
     return true; 
    } catch (IOException e) { 
     // Log error here or.... 
     throw new RuntimeException(e.getMessage(), e); // Convert to RuntimeException 
    } finally { 
     if (fos != null) { 
      try { 
       fos.close(); 
      } catch (IOException e2) { 
       // Log failure - no need to throw though 
      } 
     } 
    } 
} 
+1

Вот что мне нужно! Большое спасибо! – BraginiNI

1

Вы не можете изменить подпись метода при переопределении метода. Поэтому вам нужно обработать исключение в переопределенном методе, а не бросать его.

Это содержит хорошее объяснение о том, как использовать и попытаться поймать: http://download.oracle.com/javase/tutorial/essential/exceptions/try.html

+0

+1 Точно. Ваш новый метод должен быть «заменой» замены существующего в терминах подписи, а это означает, что с внешней точки зрения он не должен бросать какие-либо (проверенные) исключения. Это часто может быть немного больно, и я не думаю, что есть какой-либо вполне удовлетворительный общий подход для преодоления этого. –