Как считать данные из файла в LinkedHashMap?
Имеется:
Map<String, Card> cardsMap = new LinkedHashMap<>();
Где класс Card выглядит таким образом:
public class Card {
private String definition;
private int mistakes;
public Card(String definition) {
this.definition = definition;
}
public String getDefinition() {
return definition;
}
public void setDefinition(String definition) {
this.definition = definition;
}
public int getMistakes() {
return mistakes;
}
public void setMistakes(int mistakes) {
this.mistakes = mistakes;
}
}
Записываю в файл данные следующим способом:
public static void saveGame() throws IOException {
System.out.println("File name:");
String fileName = reader.readLine();
int count = 0;
File exportCards = new File(fileName);
FileOutputStream fos = new FileOutputStream(exportCards);
PrintWriter pw = new PrintWriter(fos);
for (Map.Entry<String, Card> element : Cards.getCardsMap().entrySet()) {
pw.println(element.getKey() + "=" + element.getValue().getDefinition() + "=" + element.getValue().getMistakes());
count++;
}
pw.flush();
pw.close();
fos.close();
System.out.println(count + " cards have been saved.");
}
Получаю на выходе *.txt с данными (к примеру):
black=черный=0
white=белый=1
green=зеленый=1
red=красный=2
Как мне теперь правильно считать такого формата данные и запихнуть в LinkedHashMap, при этом обновив данные в таблице, а не полностью их переписывая.
merge - да, stringTokenizer.... возникли сложности как записать значения definition и mistakes
Или использовать другой способ записи, какой? Как правильно сериализировать такой LinkedHashMap?
Ответы (2 шт):
Попробуйте код наподобие этому.
try
{
File file=new File("Demo.txt"); //creates a new file instance
FileReader fr=new FileReader(file); //reads the file
BufferedReader br=new BufferedReader(fr); //creates a buffering character input stream
String line;
Map<String, Card> cardsMap = new LinkedHashMap<>();
while((line=br.readLine())!=null)
{
String [] lineChunks = line.split("=");
Card element = new Card(lineChunks[1]);
element.setMistakes(parseInt(lineChunks[2]));
cardsMap.put(lineChunks[0], element);
}
fr.close(); //closes the stream and release the resources
}
catch(IOException e)
{
e.printStackTrace();
}
Показывать, как использовать стандартные классы производные Reader я не вижу смысла. во-первых, таких примером уже валом, только ленивый не найдет. во-вторых, вряд ли это будет еще кому-то интересно, в т.ч. по причине номер 1. посему хочу показать более интересное решение. для начала делаем то, что приводит в дикий восторог любого ООП-ешника : создаем интерфейс. Именно в интерфейсе мы реализуем методы, отвечающие за чтение-запись, унаследуемся от Serializable. Наша задача сохранять и читать в/из текстового файла объект типа Map, в виде ключа использующий произвольную строку, а в виде значения объект-наследник нашего интерфейса(обеспечим это с помощью дженерика). Разделитель мы принимаем в параметрах методов, осуществляющих чтение-запись, как и имя файла. Мы не знаем, кто унаследует наш интерфейс, поэтому для метода сохранения должны обеспечить возможность преобразования объектов класса-наследника в строку с разделителем, который мы получаем в аргументах. Для этого напишем один метод без реализации - public String toLine(String delimiter), который и будет вызывать наш метод сохранения, получая от конкретного объекта его строковое представление с указанным в аргументах разделителем. Такой вызов (вызов абстрактного метода из реализованного метода ) называется шаблонный метод и является паттерном проектирования. С чтением все сложнее, потому что нам нужно создать коллекцию объектов, которые являются наследниками нашего интерфейса. Причем классов-наследников может быть много , что не позволяет нам написать new ... В этому случае используем лямбду, которая и будемт отвечать за преобразование массива строк в конкретный объект - наследник. Выглядит это так:
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public interface Storable extends Serializable{
public static <T extends Storable> void save(String fileName, Map<String, T> map, String delimiter) throws IOException {
Files.write(Paths.get(fileName), map.entrySet().stream()
.map(entry->String.join(delimiter, entry.getKey(), entry.getValue().toLine(delimiter)))
.collect(Collectors.joining("\r\n")).getBytes());
}
public static <T extends Storable> Map<String, T> load(String fileName,
String delimiter, Function <String[], T> function) throws IOException {
try (Stream<String> stream = Files.lines(Paths.get(fileName), Charset.defaultCharset())) {
return stream.map(line -> line.split(delimiter))
.collect(Collectors.toMap(k -> k[0],
v -> function.apply(Arrays.copyOfRange(v, 1, v.length)),
(e1, e2) -> e2, LinkedHashMap::new));
}
}
public String toLine(String delimiter);
}
А теперь унаследуем наш класс Card от созданного интерфейса и переопределим абстрактный метод.
public class Card implements Storable{
private static final long serialVersionUID = -7729372580030662112L;
private String definition;
private int mistakes;
public Card(String definition, int mistakes) {
this.definition = definition;
this.mistakes = mistakes;
}
public Card(String [] values) {
this.definition = values[0];
this.mistakes = Integer.valueOf(values[1]);
}
@Override
public String toLine(String delimiter){
return String.join(delimiter, definition, String.valueOf(mistakes));
}
public static void main(String[] args) throws IOException {
Map<String, Card> map = new LinkedHashMap<>();
map.put("black", new Card("черный", 0));
map.put("white", new Card("белый", 1));
map.put("green", new Card("зеленый", 1));
map.put("red", new Card("красный", 2));
String fileName = "file.txt";
String delimiter = "=";
Storable.save(fileName, map, delimiter);
Map<String, Card> loadMap = Storable.load(fileName, delimiter, values->new Card(values));
System.out.println(loadMap);
}
}
В итоге, наследуясь от созданного нами интерфейса, мы можем выполнять аналогичные операции (чтения/записи) , переиспользуя код интерфейса , тем самым упростим разработку, избежим дублирование кода.