Search code examples
javafilestreaminputstreamreverse

How to reverse a InputStream in Java?


Suppose i have a input stream and want to reverse it !

I similar question How to get the content of an input stream in reverse order? but firstly thats about 8 years old and also its not what i want exactly!

Suppose i have a InputStream like :

FileInputStream in = new FileInputStream(new File("some_random_file.bin"));

Note this is not specifically for Text files but for binaries!

Now,

I have a way of reversing it :

public static InputStream reverseStream(InputStream in) throws Exception{
    byte bytes[] = in.readAllBytes();
    byte bytesRev[] = new byte[bytes.length];
    for(int i=bytes.length - 1, j = 0;i >= 0; i--, j++)
        bytesRev[i] = bytes[j];
    return new ByteArrayInputStream(bytesRev);
}

But i am not sure this is the most efficient way to do it!

I want to have a efficient way to achieve this even for Large Files!


Solution

  • If you're willing to read the entire file into memory, then your solution is pretty good. The memory footprint can be improved by reversing the contents in placed rather than allocating a second array to store the reversed contents:

    public static InputStream reverseStream(InputStream in) throws Exception{
        byte bytes[] = in.readAllBytes();
        for(int i=bytes.length - 1, j = 0;i >j; i--, j++) {
            byte tmp = bytes[i];
            bytes[i] = bytes[j];
            bytes[j] = tmp;
        }
        return new ByteArrayInputStream(bytes);
    }
    

    If the file is so large that you don't want to load it all at once, then you will need to use class java.io.RandomAccessFile to read the file in reverse order. You will need to use some sort of internal buffering to avoid horrible performance. You can wrap this up in your own implementation of InputStream that reads backwards through the buffer, loading a new buffer-full on the fly as necessary.

    Here's my stab at a class that does this. This code is completely untested (although it compiles).

    /**
     * An input stream that reads a file in reverse. (UNTESTED)
     *
     * @author Ted Hopp
     */
    class ReverseFileInputStream extends InputStream {
        private final RandomAccessFile file;
        /** Internal buffer for reading chunks of the file in reverse. */
        private final byte[] buffer;
        /** Position of the start of the buffer in the file. */
        private long bufferPosition;
        /** Position of the next byte to be read from the buffer. */
        private int bufferCursor;
    
        public ReverseFileInputStream(File file, int bufferSize) throws IOException {
            this.file = new RandomAccessFile(file, "r");
            buffer = new byte[bufferSize];
            bufferPosition = this.file.length();
            bufferCursor = -1;
        }
    
        @Override public int read() throws IOException {
            if (bufferCursor < 0) {
                fillBuffer();
            }
            return bufferCursor < 0 ? -1 : (buffer[bufferCursor--] & 0xff);
        }
    
        @Override public void close() throws IOException {
            file.close();
        }
    
        private void fillBuffer() throws IOException {
            if (bufferPosition > 0) {
                long newBufferPosition = Math.max(0L, bufferPosition - buffer.length);
                bufferCursor = (int) (bufferPosition - newBufferPosition);
                file.seek(newBufferPosition);
                file.readFully(buffer, 0, bufferCursor--);
                bufferPosition = newBufferPosition;
            }
        }
    }
    

    Note that if you try to wrap a Reader around this, the result will likely be nonsense unless the text encoding of the underlying file is one byte per character. Likewise with DataInputStream, etc.