while loop with a readLine() - java

Here is my question: normal while loop looks like this -
while(condition){statements}
and statements execute until condition becomes false and the loop is over. I'm interested in logic of this kind of statement:
while((a=b.readLine) != null) { ... }
It's used in client - server communication in my case. The condition is sometimes true, sometimes isn't, but loop looks like it's testing the condition forever and when true, statements in {} execute. It looks to me that loop waits for the condition to be true and then runs statements. Is it somehow linked to the way BufferedReader and InputStreamReader work or what? Also, it seems this loop is never over, it just waits for a condition to be true and then runs statements, and then again waits for a condition to be true, etc. I would be thankful for any clarification.

while((a = b.readLine()) != null){ ... }
At each iteration, the condition inside the parentheses is evaluated.
Evaluating this expression consists in calling b.readLine(), affecting the returned value to a, and comparing a with null.
Calling readLine(), as documented, consists in blocking until the next line is read. If there is no next line, readLine() returns null.
So, in short, this while loop reads every line from the reader, does something with the line (inside the while block) and stops when the end of the stream is reached.

The following code snippet
String a;
while((a = b.readLine()) != null) { // a = b.readLine() -> a -> a != null
...
}
is equivalent to
String a = b.readLine();
while(a != null) {
...
a = b.readLine();
}
but the first way is more simple, readable, and Java allows using assignment operators here because they return the value of a renewed variable.

This idiom
while ( (a = b.readLine()) != null) { /* body */ }
is a normal while loop, but the condition happens to contain an embedded assignment operator, which yields the result of the assignment, which is compared to null.
Whether b is reading from a socket, a file, or any other input stream, presumably b is something like a BufferedReader with its readLine() method. This method will only return null when the end of the stream is reached. It will block if the end of the stream hasn't been reached yet, but not further input consisting of a newline character has been read off the stream yet.
When such a line is available, a is not null and the body of the loop executes. The result of readLine is assigned to a for convenient processing withing the body of the loop.
When the end of the stream is reached, a is null and the while loop ends.
This idiom allows for easy processing of an entire stream of data, whether it is from an entire file, from an entire socket connection, or otherwise generally reading from an entire input stream of data. It looks more complicated than a simpler while loop, but it's just a standard while loop with a more complicated condition.

Because most IO streams don't read all data at once but bits-by-bits, putting the read() or readLine() assignment as part of the WHILE condition ensures that the WHILE block continues to loop as the input stream is read bits-by-bits until there's nothing to left to read (that is, all data have been read).

Related

Postfix to infix optimization

I am trying to build a syntax from a postfix input. This is my current code, and it's not quite fast enough. t is a BufferedInputStream object, and the readNext() reads the next number in the input.
Any ideas how to optimize the performance of this code, or another way of doing it that is faster?
for(int j = t.read();t.available()>0;j=t.read()) {
if(j==32) continue;
if(j==43||j==45||j==42||j==47) {
Node i = stack.pop();
Node k = stack.pop();
stack.push(new Node(k,i,j));
}else {
int number = readNext(j);
stack.push(new Node(number));
}
}
Do not use .available(). It's a weird method. It does what its specification says it does, but if you carefully read that specification, you realize that what it does is effectively useless. Specifically, available() can return 0 even though the stream hasn't closed yet. (This is trivial to show by reading from standard in and not typing for a while). The right way to detect end-of-input is to check the number that read() returns: It'll return -1 when the stream ends.
As to your question: Nothing in this code is slow. So, either it's already going as fast as possible, or, the inputstream you're reading from has inherent inefficiencies which I can't tell you about because you haven't shown that part of the code, or, that readNext() method is the source of the slowness.

Having issue with the concept of extra semi-colon at the end of for loop in Java

I was writing a code to insert an element at the end of the linked list. However without using the semi-colon at the end of for, I'm unable to get the list updated properly, what is the significance of this semi-colon and how is this affecting my code?
public void insertAtEnd() throws IOException {
LinkedList node=new LinkedList();
System.out.println("Enter an element");
int value=Integer.parseInt(br.readLine());
node.setData(value);
LinkedList p,q;
for(p=head; (q=p.getNext())!=null; p=q);
p.setNext(node);
}
The semicolon means that the statement below won't be executed until the loop has exited. Your case, the loop is taking p to the end of the list (the last element) and then the next statement is assigning its next value to the new element
Well, the way it is constructed right now as for(p=head; (q=p.getNext())!=null; p=q);, the for loop has an empty body. The variables p and q through the p.getNext() and p=q assignments are updating. Then it is executing the p.setNext(node); a single time.
EDIT: As to why the code is working with the semi-colon, it is because you are advancing the variables p and q over the list. While the for loop with the empty body is fine, I think traditionally one sees a while loop.
The indentation of the p.setNode(node); makes it appear as if the statement were related to the for loop, but it really isn't, as the goal is to find the end of the linked list by iterating over it.
(note: others made similar points while I was typing this answer)
it needs to be removed, it is causing it to do nothing in the loop.
The loop will execute the next single block of code after the loop. Either one statement, or one { } code block.
In this case, you have one statement ;, so it "executes" and does nothing, and then will call your p.setNext(node); after the loop ends.
public void insertAtEnd() throws IOException {
LinkedList node=new LinkedList();
System.out.println("Enter an element");
int value=Integer.parseInt(br.readLine());
node.setData(value);
LinkedList p,q;
for(p=head; (q=p.getNext())!=null; p=q)
p.setNext(node);
}
The semicolon is essentially transforming what you appear to want to do because of your indentation:
for(p=head; (q=p.getNext())!=null; p=q)
{
p.setNext(node);
}
into the following:
for(p=head; (q=p.getNext())!=null; p=q)
{
}
p.setNext(node);
So with the semicolon your loop is executing nothing at every iteration. Then, after it has finished executing, you're running p.setNext(node); exactly once.
Usually you can avoid this problem in the future by explicitly writing your curly braces as I have done in these two code segments. It is unclear exactly which code segment you are trying to accomplish right now.
The for statement provides a compact way to iterate over a range of values. Programmers often refer to it as the "for loop" because of the way in which it repeatedly loops until a particular condition is satisfied. The general form of the for statement can be expressed as follows:
for (initialization; termination; increment) {
//statement(s)
}
When using this version of the for statement, keep in mind that:
The initialization expression initializes the loop; it's executed once,
as the loop begin
When the termination expression evaluates to false, the loop terminates.
The increment expression is invoked after each iteration through the loop; it is perfectly acceptable for this expression to increment or decrement a value.
In your case, the loop is taking p to the end of the list (the last element) and then the next statement is assigning its next value to the new element

NIO Selector OP_READ and OP_WRITE, some questions about handling them

I've been following this tutorial, it's quite helpful and most things are making sense.
If you look on page 34 you'll see this snippet of code:
} else if ((key.readyOps() & SelectionKey.OP_READ)
== SelectionKey.OP_READ) {
// Read the data
SocketChannel sc = (SocketChannel)key.channel();
// ...
}
What exactly is going on here, the single ampersand means both conditions are checked, so, in this case key.readyOps() will return a number whose bits represent the operations that are available to work on. SelectionKey.OP_READ is another int. So I understand how these two relate.
So the first test (key.readyOps() & SelectionKey.OP_READ) in my head this will return true or false. true if key.readyOps() == SelectionKey.OP_READ is that correct? or is it if key.readyOps() contains the correct bits set for OP_READ. The start of that particular if statement contains the same test, but with OP_ACCEPT instead of OP_READ. You can see that on page 33.
Then the next test in the sequence == SelectionKey.OP_READ what's happening here?
In my head I see that evaluating like this:
if(true == SelectionKey.OP_READ)
or
if(false == SelectionKey.OP_READ)
But that can't be right can it? since OP_READ is an int? Or will an int be true for anything except 0? I don't think that's right either because the second part is redundant.
The above works fine, I'm just not clear on what's actually going on with the if test.
So next, I'm wondering about handling reading once the above evaluates to true.
First I call SocketChannel sc = (SocketChannel) key.channel(); to get a handle on the channel.
Then I ready my buffer and read from the socket with int bytesRead = sc.read(myBuffer);
Now I need to test what was read, correct? So I do something like this:
if(read == -1){
// The connection has been terminated, handle that.
}else if(read == 0){
// Check data is complete, if so switch to OP_WRITE
}else{
// Read bytes into the buffer
}
So is the above an acceptable thing to do?
the javadoc says the following about OP_READ:
Operation-set bit for read operations. Suppose that a selection key's
interest set contains OP_READ at the start of a selection operation.
If the selector detects that the corresponding channel is ready for
reading, has reached end-of-stream, has been remotely shut down for
further reading, or has an error pending, then it will add OP_READ to
the key's ready-operation set and add the key to its selected-key set.
the single ampersand means both conditions are checked
It means the bitwise AND of the two operands is computed. As one of the operands has only a single bit set, only one condition is being checked: OP_READ.
so, in this case key.readyOps() will return a number whose bits represent the operations that are available to work on.
Of course, but the & modifies that.
SelectionKey.OP_READ is another int. So I understand how these two relate.
What follows doesn't justify your confidence about that.
So the first test (key.readyOps() & SelectionKey.OP_READ) in my head this will return true or false.
Wrong again. It will return zero or SelectionKey.OP_READ. You need to brush up the rules of Java arithmetic.
true if key.readyOps() == SelectionKey.OP_READ is that correct?
No.
or is it if key.readyOps() contains the correct bits set for OP_READ.
See above.
The start of that particular if statement contains the same test, but with OP_ACCEPT instead of OP_READ ... Then the next test in the sequence == SelectionKey.OP_READ what's happening here?
It is testing first for OP_ACCEPT and then for OP_READ.
In my head I see that evaluating like this:
No. See above. You are confused. What you have posted doesn't make sense.
if(true == SelectionKey.OP_READ)
true can't be equal to an integer.
or
if(false == SelectionKey.OP_READ)
Ditto.
But that can't be right can it? since OP_READ is an int?
Correct. So you can't think it.
Or will an int be true for anything except 0?
No. You need to brush up the rules of Java arithmetic.
So next, I'm wondering about handling reading once the above evaluates to true.
First I call SocketChannel sc = (SocketChannel) key.channel(); to get a handle on the channel.
No, to get the SocketChannel from the SelectionKey.
Then I ready my buffer and read from the socket with
int bytesRead = sc.read(myBuffer);
Now I need to test what was read, correct?
You need to test the result returned by the read() method.
So I do something like this:
if(read == -1){
// The connection has been terminated, handle that.
}else if(read == 0){
// Check data is complete
No. If the data was already complete you should already know that, and you wouldn't have called read() again.
// if so switch to OP_WRITE
No. OP_WRITE is used when a write() has returned zero. No other time.
}else{
// Read bytes into the buffer
No. The bytes are already in the buffer.
So is the above an acceptable thing to do?
It doesn't begin to make sense. You need to take another good look at the IBM tutorial you cited. The misconceptions you've posted here don't appear there.

Does bufferedReader close the reader after reaching EOF?

I have a piece of code that uses a BufferedReader to read from a file and marks the current position of the reader so that I can reset to the last line when I need to. However I'm getting an I/O exception and I'm not positive why. This piece of code is in a bigger loop that also utilizes the read method which is why I'm resetting when I encounter "From:".
while((line=reader.readLine())!=null && line.indexOf("From:")!=0) {
emailMsg.append(line).append("\n");
reader.mark(0);
}
reader.reset(); //io exception: mark invalid
emailMsg.append(".\n");
The argument passed to mark() is the readAheadLimit. From the documentation:
readAheadLimit - Limit on the number of characters that may be read while still preserving the mark. An attempt to reset the stream after reading characters up to this limit or beyond may fail. A limit value larger than the size of the input buffer will cause a new buffer to be allocated whose size is no smaller than limit. Therefore large values should be used with care.
Since you have specified 0, and the next readLine() call ends up reading something from the stream, the subsequent reset has a possibility of failing since you are over the limit.
When you tell the stream to "mark" a location, you specify a read-ahead limit, which basically tells the stream to "remember" that many characters. As long as you don't read past the limit, you can call reset() how many ever times you want and it will reset it back to the beginning of that "remembered" set of characters. Once you read past that limit however, the mark is invalid and so subsequent calls to reset() will fail. In your case, you have specified a read-ahead limit of 0, which means any read past the current point will invalidate the mark, which is what you are seeing.
To get around this, specify a non-zero read-ahead limit.
To me it looks, like the line you read from reader does not contain "From:" sequence, so execution does not enter while-loop, so the mark is not set, and when execution continues right after while-loop body, reset throws an exception, because the mark was not set, as per reset() documentation.
You rather need this:
boolean marked = false;
while ((line = reader.readLine()) != null) {
if (line.indexOf("From:") != 0) {
continue;
}
emailMsg.append(line).append('\n');
reader.mark(0);
marked = true;
}
if (marked) {
reader.reset();
emailMsg.append('\n');
}

When should I use a 'while loop'?

I just stumbled upon this question Are "while(true)" loops so bad?
They made me think what do I normally do.And to my surprise I realised that I have almost never used a while loop in my professional code(or work code) .
Front end frameworks e.g faces etc do not count.
So When should I use a 'while loop'? and
How often do you use while loop? It's is a real question please do not close as being subjective I really am after a concrete example.where it can not be replaced with a better alternate.
One place where I might use it is where you need to treat the first element of a sequence differently to the rest. That makes a foreach loop awkward, but a while loop works well:
Iterator<String> iterator = foo.iterator();
// Handle the first item specially
if (!iterator.hasNext()) {
throw new SomeException("foo shouldn't be empty");
}
SomeResult result = new SomeResult(iterator.next());
// Now deal with the rest
while (iterator.hasNext())
{
String item = iterator.next();
result.addItem(item);
}
Also I use a while loop as one of the few places where I'll also include an assignment in a condition:
String line;
while ((line = reader.readLine()) != null)
{
// Handle the line
}
or with an InputStream:
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1)
{
// Handle the buffer
}
java.util.Scanner scanner = //...
while(scanner.hasNextLine()) {
String line = scanner.nextLine();
//..do sth with the line
}
In fact every while loop can be replaced with for. But e.g. in the code above it would be less readable - and that's the point: use while when it fits better to the nature of the problem.
You should use it to loop while some condition holds true.
Simple never-stopping backend logic:
while (true) {
consumeMessage();
}
Or also
for (;;) {
consumeMessage();
}
You should use it when you dont know how many iterations will be needed.
You only know that you want to do something while your condition is met. It could be itereated 2, 100, 0... times.
Of course you can always rewrite a while loop into a for loop, but often it is uglier, meaning that parts of the for (..;..;..) are left blank - mainly the initialization. Findbugs also gives a warning in this case: similar to "simple for loop detected, rewrite it as a while loop".
The main application of the while loop is that you do not need an initialization, or want to treat the first loop iteration (e.g. first element of an enumeration) specially, in which case you do the initialization beforehand, too.
Use it when you have a main loop in your code which you want to run until something changes.
When you dont need a counter, and when you dont need to iterate over a collection (because then you need a counter).
Using a for(;whatever;) is ugly code, thats where you have to use a while.
Also the variation, do ... while allows you to do something at least once and then possibly many times.

Categories