2
public void lock() {
    if (this.isLocked()) return;

    try {
        this.dataOut.flush();
        this.dataOut.close();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    DataInputStream inputStream =
        new DataInputStream(
        new BufferedInputStream(
        new ByteArrayInputStream(
            this.byteOut.toByteArray())));

    IntStream.Builder intStreamBuilder = IntStream.builder();
    try {
        try {
            while (true) {
                intStreamBuilder.accept(inputStream.readInt());
            }
        } catch (EOFException e) {
            // logic to be executed after stream has been fully read
            int[] pool = intStreamBuilder.build().toArray();
            super.lock(pool);
        } finally {
            inputStream.close();
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

What I do here is take an DataOutputStream containing Integers, flush its remaining contents into a ByteArrayOutputStream named this.byteOut and then build an IntStream from it.

I'm from the C# domain and in the process of learning Java, so the code here does not have any actual purpose.

Is there any way to do what I do here more elegantly in Java?

My two main concerns are:

  • The way I determine that the DataInputStream has been fully read is by catching an EOFException and putting the logic to be executed after reading inside a catch block. I don't like that, since I suppose throwing and catching exceptions is somewhat expensive? Is there a better way to determine that the stream doesn't contain any more Integers?
  • The fact that I have to wrap a try-catch block around a try-catch block just to be able to call inputStream.close() in the inner finally block. Is there a solution that is not so clunky?
Michael Schnerring
  • 3,584
  • 4
  • 23
  • 53
  • 5
    For your second point, you may want to look into [*try-with-resources*](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html). – Oliver Charlesworth Dec 04 '17 at 19:34
  • @OliverCharlesworth Goshhhh, this is something I was missing a lot in Java being from C#. `using (var ms = new MemoryStream())` is the C# equivalent to this. Thanks so much – Michael Schnerring Dec 04 '17 at 19:39
  • Also, you can have multiple catch blocks and a finally block associated with a single try. IIRC the most specific catch should be listed before more generic catches (With EOFException more specific than IOException, for example). – Gus Dec 04 '17 at 21:57
  • 1
    @Gus I know but as soon as you put a statement inside the `finally` block that possibly throws a checked exception you need to wrap it in another `try-catch`. The resources construct takes care of the `stream.close()` for you and thus eliminates the otherwise needed extra wrapping. – Michael Schnerring Dec 04 '17 at 22:22

3 Answers3

1

It's mostly you. If you don't like the try with resources construct, you can still combine all of your try statments and stack the catch blocks.

public void lock()
{
    DataInputStream inputStream = null;
    IntStream.Builder intStreamBuilder;

    if (isLocked())
    {
        return;
    }

    try
    {
        inputStream = new DataInputStream(
            new BufferedInputStream(
                new ByteArrayInputStream(
                    byteOut.toByteArray())));

        intStreamBuilder = IntStream.builder();

        dataOut.flush();
        dataOut.close();

        while (true)
        {
            intStreamBuilder.accept(
                inputStream.readInt());
        }
    }
    catch (IOException exception)
    {
        throw new RuntimeException(exception);
    }
    catch (EOFException ignoredException)
    {
        // logic to be executed after stream has been fully read
        int[] pool = intStreamBuilder.build().toArray();
        super.lock(pool);
    }
    finally
    {
        if (inputSream != null)
        {
            try
            {
                inputStream.close();
            }
            catch (IOException exception)
            {
                throw new RuntimeException(exception);
            }
        }
    }
}

The try inside the finally is required.

I prefer the try-with-resources construct.

DwB
  • 37,124
  • 11
  • 56
  • 82
  • oh I do like the resources construct, I just was not aware that it was part of `try` itself. In C# it's a separate construct called `using`. See my answer for my preferred solution. – Michael Schnerring Dec 04 '17 at 22:03
0

If I were you I will change my code to be as the following

 //try-with-resources-close
 try (DataInputStream inputStream =
                 new DataInputStream(
                         new BufferedInputStream(
                                 new ByteArrayInputStream(
                                         this.byteOut.toByteArray())))) {

        IntStream.Builder intStreamBuilder = IntStream.builder();
        byte[] byts = new byte[4];
        while (inputStream.read(byts) > -1) {// read  4 bytes and convert them to int
            int result = ByteBuffer.wrap(byts).getInt();
            intStreamBuilder.accept(result);
        }
        // logic to be executed after stream has been fully read
        int[] pool = intStreamBuilder.build().toArray();
        super.lock(pool);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

try-with-resources this new way added in Java 7 to close any object that implements AutoCloseable automatically when try block is executed

readInt method it works as the following read 4 bteys then convert them to int

    int ch1 = in.read();
    int ch2 = in.read();
    int ch3 = in.read();
    int ch4 = in.read();
    if ((ch1 | ch2 | ch3 | ch4) < 0)
        throw new EOFException();
    return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));

//So you also can do the same thing, but instead of throws EOFException you can add return false or break the loop

for read bytes method if no bytes existed then it reture -1

Ahmad Al-Kurdi
  • 2,248
  • 3
  • 23
  • 39
  • I thought of copying `readInt` but think it's a bad idea because I'd copy an implementation detail of the framework? While researching an answer myself I came across the `IntBuffer` class that is even simpler than your `ByteBuffer` solution. See my answer for that. – Michael Schnerring Dec 04 '17 at 22:06
0

Well the better solution I found thanks to @Oliver Charlesworth is the following:

    try (DataInputStream inputStream =
        new DataInputStream(
        new BufferedInputStream(
        new ByteArrayInputStream(this.byteOut.toByteArray())))) {
        while (true)
            intStreamBuilder.accept(inputStream.readInt());
    } catch (EOFException e) {
        int[] pool = intStreamBuilder.build().toArray();
        super.lock(pool);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

This still has logic inside the catch block the code look definitely cleaner.

However, I cannot come up with a better approach that determines that the InputDataStream has been fully read.

What bugs me about this is that reaching the end of the stream is expected and it would actually be exceptional if no exception was thrown, what IMO defeats the purpose of exceptions in the first place.


I asked a separate question of the possible use of Java NIO's IntBuffer class here. My above snippet could be changed to:

IntBuffer intBuffer = ByteBuffer.wrap(this.byteOut.toByteArray()).asIntBuffer();
while (intBuffer.hasRemaining()){
    intStreamBuilder.accept(intBuffer.get());
}
int[] pool = intStreamBuilder.build().toArray();
super.lock(pool);
Michael Schnerring
  • 3,584
  • 4
  • 23
  • 53