首页 > 解决方案 > 如何将 ArrayList 存储在文件中?

问题描述

我有一个表示存储在文件中的 ArrayList 的类,因为我需要一个 ArrayList,其中包含数 GB 的数据,这显然太大而无法存储在内存中。数据由一个名为 Field 的类表示,函数 Field.parse() 仅用于将 Field 转换为字符串,反之亦然。

Field 类存储(奇怪的)棋子及其坐标的列表。

我的课程工作正常,但是向文件中添加元素需要很长时间,我需要我的程序尽可能快地运行。有谁知道更有效/更快的做事方式?

另外,我不允许使用外部库/api。请记住这一点。

这是负责将 Field 对象存储在临时文件中的类:

private File file;
private BufferedReader reader;
private BufferedWriter writer;

public FieldSaver() {
    try {
        file = File.createTempFile("chess-moves-", ".temp");
        System.out.println(file.getAbsolutePath());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void add(Field field) {
    try {
        File temp = File.createTempFile("chess-moves-", ".temp");
        writer = new BufferedWriter(new FileWriter(temp));
        reader = new BufferedReader(new FileReader(file));
        String line;

        while((line = reader.readLine()) != null ) {
            writer.write(line);
            writer.newLine();
        }

        reader.close();
        writer.write(field.parse());
        writer.close();
        file.delete();
        file = new File(temp.getAbsolutePath());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public Field get(int n) {
    try {
        reader = new BufferedReader(new FileReader(file));
        for (int i = 0; i < n; i++) {
            reader.readLine();
        }
        String line = reader.readLine();
        reader.close();
        return Field.parse(line);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

这是 Field 类:

private WildBoar wildBoar;
private HuntingDog[] huntingDogs;
private Hunter hunter;

private int size;

@Override
public String toString() {
    String result = "Wildschwein: " + wildBoar.toString();
    for (HuntingDog dog : huntingDogs) {
        result += "; Hund: " + dog.toString();
    }
    return result + "; Jäger: " + hunter.toString();
}

@Override
public boolean equals(Object obj) {
    if (obj instanceof Field) {
        Field field = (Field) obj;
        HuntingDog[] dogs = field.getHuntingDogs();
        return wildBoar.equals(field.getWildBoar()) && hunter.equals(field.getHunter()) && huntingDogs[0].equals(dogs[0]) && huntingDogs[1].equals(dogs[1]) && huntingDogs[2].equals(dogs[2]);
    }
    return false;
}

public Field(int size, WildBoar wildBoar, HuntingDog[] huntingDogs, Hunter hunter) {
    this.size = size;
    this.wildBoar = wildBoar;
    this.huntingDogs = huntingDogs;
    this.hunter = hunter;
}

public WildBoar getWildBoar() {
    return wildBoar;
}

public HuntingDog[] getHuntingDogs() {
    return huntingDogs;
}

public Hunter getHunter() {
    return hunter;
}

public int getSize() {
    return size;
}

public static Field parse(String s) {
    String[] arr = s.split(",");
    WildBoar boar = WildBoar.parse(arr[0]);
    Hunter hunter = Hunter.parse(arr[1]);
    HuntingDog[] dogs = new HuntingDog[arr.length - 2];
    for(int i = 2; i < arr.length; i++) {
        dogs[i - 2] = HuntingDog.parse(arr[i]);
    }
    return new Field(8, boar, dogs, hunter);
}

public String parse() {
    String result = wildBoar.parse() + "," + hunter.parse();
    for(HuntingDog dog : huntingDogs) {
        result += "," + dog.parse();
    }
    return result;
}

标签: javafilearraylistout-of-memory

解决方案


这是一个 MCVE,可以根据您提供的信息来做您想做的事情。

您可以运行它并看到它可以非常快速地将 a 保存Field到文件并按Field索引获取。

s 是恒定长度,因此Field您可以Field通过索引的字节偏移量乘以字段长度(以字节为单位)来获取索引。如果字段不是恒定长度,这将更加困难。

import java.io.Closeable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class FieldSaver implements Closeable {

    public static void main(String[] args) throws IOException {
        File f = File.createTempFile("chess-moves-", ".temp");
        try (FieldSaver test = new FieldSaver(f);) {
            for (byte i = 0; i < 100; i++) {
                test.add(new Field(8, new WildBoar(i, i), new Hunter(i, i), new HuntingDog[] {
                        new HuntingDog(i, i),
                        new HuntingDog(i, i),
                        new HuntingDog(i, i) }));
            }

            // Get a few Fields by index
            System.out.println(test.get(0));
            System.out.println(test.get(50));
            System.out.println(test.get(99));

            // EOF exception, there is no Field 100
            // System.out.println(test.get(100));
        }
    }

    private final RandomAccessFile data;

    public FieldSaver(File f) throws FileNotFoundException {
        data = new RandomAccessFile(f, "rw");
    }

    public void add(Field field) throws IOException {
        data.seek(data.length());
        field.write(data);
    }


    public Field get(int index) throws IOException {
        data.seek(index * Field.STORAGE_LENGTH_BYTES);
        return Field.read(data);
    }

    public void close() throws IOException { data.close(); }


    static abstract class Piece {
        protected byte xPos;
        protected byte yPos;

        public Piece(DataInput data) throws IOException {
            xPos = data.readByte();
            yPos = data.readByte();
        }

        public Piece(byte xPos, byte yPos) {
            this.xPos = xPos;
            this.yPos = yPos;
        }

        public void write(DataOutput data) throws IOException {
            data.writeByte(xPos);
            data.writeByte(yPos);
        }

        public String toString() { return "[" + xPos + ", " + yPos + "]"; }
    }

    static class Hunter extends Piece {
        public Hunter(byte xPos, byte yPos) { super(xPos, yPos); }
        public Hunter(DataInput data) throws IOException { super(data); }
    }

    static class HuntingDog extends Piece {
        public HuntingDog(byte xPos, byte yPos) { super(xPos, yPos); }
        public HuntingDog(DataInput data) throws IOException { super(data); }
    }

    static class WildBoar extends Piece {
        public WildBoar(byte xPos, byte yPos) { super(xPos, yPos); }
        public WildBoar(DataInput data) throws IOException { super(data); }
    }

    static class Field {
        // size of boar + hunter + 3 dogs
        public static final int STORAGE_LENGTH_BYTES = 2 + 2 + (3 * 2);

        private int size;
        private WildBoar boar;
        private Hunter hunter;
        private final HuntingDog[] dogs;

        public Field(int size, WildBoar wildBoar, Hunter hunter, HuntingDog[] huntingDogs) {
            this.size = size;
            this.boar = wildBoar;
            this.hunter = hunter;
            this.dogs = huntingDogs;
        }

        public String toString() {
            String result = "Wildschwein: " + boar.toString();
            for (HuntingDog dog : dogs) {
                result += "; Hund: " + dog.toString();
            }
            return result + "; Jäger: " + hunter.toString();
        }

        public static Field read(DataInput data) throws IOException {
            WildBoar boar = new WildBoar(data);
            Hunter hunter = new Hunter(data);
            HuntingDog[] dogs = new HuntingDog[3];
            for (int i = 0; i < 3; i++) {
                dogs[i] = new HuntingDog(data);
            }
            return new Field(8, boar, hunter, dogs);
        }

        public void write(DataOutput data) throws IOException {
            boar.write(data);
            hunter.write(data);
            for (HuntingDog dog : dogs) {
                dog.write(data);
            }
        }
    }
}

推荐阅读