Search code examples
javaiohashmapfileinputstream

Serializing a HashMap but Text File for input is clearing self every program run. -- Java


I have already looked at other problems with these but I don't think any of them match mine. The gist of my program is to create a HashMap of stocks (key is ticker and value is a Stock object) and then when the program ends to export the hashMap into a text file. Next time I run the program I would read in the hashMap and continue the program. So far all my other functionalists work and I can look at the text file after I run the program and I see some code in there but when I run again the text file is cleared.

I suspect that when I declare a new FileInputStream and ObjectInputStream they are somehow deleting the information in that text file and making it empty.

My code is as follows:

     stockInfo = new HashMap<String, Stock>(10000);
     Scanner in = new Scanner(System.in);
     File file = new File("mp4output.txt");
     fos = new FileOutputStream(file);
     oos = new ObjectOutputStream(fos);
     fis = new FileInputStream(file);
     ois = new ObjectInputStream(fis);

Is how I declare my I/O streams to read in the hashMap.

Next I try to actually read in the hashMap with

           try {
           while (fis.available() > 0)   {
                 Stock test = (Stock) ois.readObject(); 
                 System.out.println("Stock: " + test.getCompany());
                 System.out.println("High: " + test.getHigh());
                 System.out.println("Volume: " + test.getHigh());
                 System.out.println("Low: " + test.getLow());
                 System.out.println("Close: " + test.getClose());
                 System.out.println("Open: " + test.getOpen());
                 System.out.println("Range: " + test.getRange());
                 System.out.println("52 Week average: " + test.getFiftyAvg());
                 System.out.println("Current Price: " + test.getcurrentPrice());
                 }
           }
           catch (Exception ex) {
                 ex.printStackTrace();
           }

However it never runs this because fis.available() always returns 0 because the file empties itself.

I feel like I have made a very dumb error somewhere but I cannon find it. Any help would be appreciated!


Solution

  • The FileOutputStream will - by default (or, whenever not created for appending) - truncate the output file. This truncation happens when the FOS is created, and before the FIS has a chance to read the data.

    A general solution in a case like this - where the input and output are to the same file - is to read all the input, close the input stream, and then open the output stream and write the new data. In this case the truncation behavior works just fine.

    With this in mind, a skeleton may look like this:

    Map<String, Stock> readStocks (ObjectInputStream ois) {
      // Read all
    }
    
    void writeStocks (ObjectOutputStream oos, Map<String, Stock> stocks) {
      // Write all
    }
    
    Map<String, Stock> stocks;
    // Use try-with-resources (Java 7+) to make life easier;
    // the OIS (and underlying FIS) are guaranteed to be closed
    // after this block ends.
    try (ObjectInputStream ois = new ObjectInputStream(
             new FileInputStream(file)) {
      stocks = readStocks(ois, stocks);
    }
    
    // Make changes to loaded data
    // (i.e. in accordance with user-supplied input)
    updateStockData(stocks);
    
    try (ObjectOutputStream oos = new ObjectOutputStream(
             new FileOutputStream(file)) {
      writeStocks(oos, stocks);
    }
    

    The Object Input/Output Streams are merely wrappers over the underlying FIS/FOS, but aren't responsible for the lower-level truncation or IO.

    Also, while OIS/OOS are probably sufficient for this task, I would recommend using JSON because using JSON is "relatively painless" with POJO mappers (e.g. see Gson), and - really why I recommend it - the output is human consumable text.


    Also, it's not well-defined across operating systems as to how multiple FIS/FOS objects on the same source work. The documentation leaves this vague note (but doesn't specify anything about interactions between FIS/FOS objects where an exception is not thrown).

    A file output stream is an output stream for writing data to a File or to a FileDescriptor. in particular, allow a file to be opened for writing by only one FileOutputStream (or other file-writing object) at a time. In such situations the constructors in this class will fail if the file involved is already open.