1

I am trying to create a program that reads a .dat file containing a HashMap object. If the .dat file is empty, it should write an empty HashMap object to the file, then, it should give the user options to edit the HashMap object.

When I tried this for the first time with an empty .dat file "playlist.dat", it attempted to write an empty HashMap object to the file, but then triggered an EOFException. I looked in the file and saw that it was empty.

The code handling this is here:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Scanner;

@SuppressWarnings("unchecked") //was the only way to stop the error: "Type safety: Unchecked cast from Object to HashMap<String,Song>" from happening

public class Main {
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        // Map of songs
        HashMap<String, Song> playList = new HashMap<String, Song>();

        // getting file name where it is stored
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the file name you stored/will store your playlist in: ");
        String fileName = input.nextLine();
        input.close();

        File file = new File(fileName);
        
        ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file))); //using output streams to find the file requested by user

        if (file.length() == 0) {
            out.writeObject(playList);
            //System.out.println("Wrote playlist"); //debug
        } //sees if there is nothing in the file, and if there is nothing, writes a blank HashMap to it

        ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); //This is where the EOFException is taking place. In the if statement above, it tests if the file is empty. Since playlist.dat is empty, it will attempt to write an empty HashMap playlist to the file, but since there is an EOFException and playlist.dat is empty, it is clearly not doing this.
        
        playList = (HashMap<String, Song>) in.readObject();
        System.out.println("Opening playlist at " + fileName + "."); //debug
        in.close();

/* other code continues this way... */
Abra
  • 19,142
  • 7
  • 29
  • 41
iNoob
  • 27
  • 8

3 Answers3

0

A couple of things:

  1. Object streams are very brittle and not ideal. Users of different versions of Java may not be able to open your HashMap in a version different from what it was serialized under. Instead, I recommend XML (already included in the JRE) or JSON (I beliee you need Jackson library... unless it has been added to the JRE)

  2. You don't need to buffer your input or output streams. To start with, buffered streams are for inefficient reading and writing (like reading by line, or writing (not all at once) to the File. But also Object streams and PrintWriter / PrintStream (which you are not using but I've included for completeness) already have buffering capacity baked in.

  3. You should definitely close your write stream before trying to read from the stream. I believe that if/when you do this, you'll see contents in the file when you read it

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
  • 1
    Just to clarify: It is the missing close(Point 3) which is the reason the file is empty. When you don't close the file the buffer don't get flushed – MTilsted Sep 11 '20 at 21:07
0

BufferedOutputStream has internal buffers with size equal 8192 bytes. Until data will exceed the buffer or you flush it whole data is being kept in the buffer thus it won't be written to the file

Seems that playlist data is smaller than buffer size - you need to call close() method on your out output stream after writing is being finished or use try-with-resources approach

m.antkowicz
  • 13,268
  • 18
  • 37
0

Basically you want to serialize an object, specifically an instance of HashMap and then de-serialize it.

Serialization in java only stores the values of the class members. It uses a combination of the actual .class file together with the .dat file to de-serialize an object. Hence if you serialize an empty HashMap, the .dat file will be [almost] empty. In your case it is empty because you didn't close the file after writing it.

Below code is a minimal example of serializing and de-serializing a HashMap. Note that it uses try-with-resources to ensure that the .dat files are closed after use. It also uses multi-catch to handle exceptions.

Explanations of the code appear after it.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;

public class SongList {

    public static void main(String[] args) {
        File f = new File("songlist.dat");
        try (FileOutputStream fos = new FileOutputStream(f);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            HashMap<String, String> playList = new HashMap<>();
            oos.writeObject(playList);
        }
        catch (IOException xIo) {
            xIo.printStackTrace();
        }
        try (FileInputStream fis = new FileInputStream(f);
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            HashMap<?, ?> playList = (HashMap<?, ?>) ois.readObject();
        }
        catch (IOException | ClassNotFoundException x) {
            x.printStackTrace();
        }
    }
}
  • BufferedOutputStream and BufferedInputStream are not required.
  • At runtime, when you de-serialize the HashMap, java has no way of knowing what the types are for the entries in the HashMap. That's why you are getting the warning. Note that it is a warning and not an error. That's why I use the ? wildcard when reading the .dat file.
  • Since you didn't post the code of class Song, I changed the HashMap value type to String so as to keep things simple. You can continue to use Song in your code.
  • Creating a new FileOutputStream creates a new, empty file. Creating a new FileOutputStream for a file that already exists will remove the file contents. If you want to append to an existing file, use this constructor of class FileOutputStream.

By the way, you should not close a Scanner that wraps standard input.

Abra
  • 19,142
  • 7
  • 29
  • 41