Why is there a prefix/postfix ++ but no prefix/postfix +=? - java

This may seem like a silly question, but why is it that in many languages there exists a prefix and postfix version of the ++ and -- operator, but no similar prefix/postfix versions of other operators like += or -=? For example, it seems like if I can write this code:
myArray[x++] = 137; // Write 137 to array index at x, then increment x
I should be able to write something like
myArray[5 =+ x] = 137; // Write 137 to array index at x, then add five to x
Of course, such an operator does not exist. Is there a reason for this? It seems like a weird asymmetry in C/C++/Java.

I'd guess there are several reasons, I think among the more heavily weighted might be:
there probably weren't thought to be too many real use cases (it may not have even occurred to some language designers in the early days)
pre/post increment mapped directly to machine operations (at least on several machines), so they found their way into the language (update: it turns out that this isn't exactly true, even if it's commonly thought so in computing lore. See below).
Then again, while the idea for pre/post/increment/decrement operators might have been influenced by machine operations, it looks like they weren't put into the language specifically to take advantage of such. Here's what Dennis Ritchie has to say about them:
http://cm.bell-labs.com/cm/cs/who/dmr/chist.html
Thompson went a step further by inventing the ++ and -- operators, which increment or decrement; their prefix or postfix position determines whether the alteration occurs before or after noting the value of the operand. They were not in the earliest versions of B, but appeared along the way. People often guess that they were created to use the auto-increment and auto-decrement address modes provided by the DEC PDP-11 on which C and Unix first became popular. This is historically impossible, since there was no PDP-11 when B was developed. The PDP-7, however, did have a few `auto-increment' memory cells, with the property that an indirect memory reference through them incremented the cell. This feature probably suggested such operators to Thompson; the generalization to make them both prefix and postfix was his own. Indeed, the auto-increment cells were not used directly in implementation of the operators, and a stronger motivation for the innovation was probably his observation that the translation of ++x was smaller than that of x=x+1.

As long as y has no side effects:
#define POSTADD(x,y) (((x)+=(y))-(y))

I'll make an assumption. There're lots of use-cases for ++i/i++ and in many the specific type of increment (pre/post) makes difference. I can't tell how many times I've seen code like while (buf[i++]) {...}. On the other hand, += is used much less frequently, as it rarely makes sense to shift pointer by 5 elements at once.
So, there's just no common enough application where difference between postfix and prefix version of += would be important.

I guess it's because it's way too cryptic.
Some argue that even ++/-- should be avoided, because they cause confusion and are responsible for most buffer overrun bugs.

Because the -- and ++ operators map to inc(rement) and dec(rement) instructions (in addition to adding and subtracting) in the CPU, and these operators are supposed to map to the instructions, hence why they exist as separate operators.

Java and C++ have pre- and post- increment and decrement operators because C has them. C has them because C was written, mostly, for the PDP-11 and the PDP-11 had INC and DEC instructions.
Back in the day, optimizing compilers didn't exist so if you wanted to use a single cycle increment operator, either you wrote assembler for it or your language needed an explicit operator for it; C, being a portable assembling language, has explicit increment and decrement operators. Also, the performance difference between ++i and i++ rarely matters now but it did matter in 1972.
Keep in mind that C is almost 40 years old.

If I had to guess, it's common to equate:
x += 5;
...with:
x = x + 5;
And for obvious reasons, it would be nonsensical to write:
x + 5 = x;
I'm not sure how else you would mimic the behavior of 5 =+ x using just the + operator. By the way, hi htiek!

Related

Unexpected Type Error when using Unary Operator --

I am learning Java and am experimenting with the unary operators --expr, expr--, and -expr.
In class, I was told that --3 should evaluate to 3. I wanted to test this concept in the following assignments:
jshell> int t = 10;
t ==> 10
| created variable t : int
jshell> int g = -3;
g ==> -3
| created variable g : int
jshell>
jshell> int d = --3;
| Error:
| unexpected type
| required: variable
| found: value
| int d = --3;
| ^
jshell> int d = --t;
d ==> 9
| created variable d : int
jshell> int f = d---t;
f ==> 0
| created variable f : int
jshell> int f = 1---t;
| Error:
| unexpected type
| required: variable
| found: value
| int f = 1---t;
| ^
| update overwrote variable f : int
My questions:
Why does assigning -3 work and not --3? I thought --3 would give 3.
Are there cases where --expr can be evaluated as double negation instead of decrement?
Why can't values suffice where the unexpected type errors were thrown?
How did Java evaluate d---t? Also, in what order?
For question 4, the way I thought of it was a right-to-left evaluation. So, if d = 9 and t is 9, the rightmost - operator is the first to act on t, making its value -9. Then the same for the second -, so then t's value becomes 9 again. Then I thought the compiler would notice that d is next to the leftmost operator and subtract the values. This would be 9-9, which evaluates to 0. Jshell shows the expression also evaluated to 0, but I want to make sure my reasoning is correct or can be improved.
-- is taken as the decrement operator. Adding spaces or using brackets will allow it to be interpreted as double negation.
int x = - -3;
//or
int x = -(-3);
Why does assigning -3 work and not --3? I thought --3 would give 3.
Java tokenizes the input. A source file consists of a sequence of relevant atomary units, which we shall call words. In y +foobar, we have 3 'words': y, +, and foobar. Tokenizing is the job of splitting them up.
This process is somewhat complicated; whitespace (obviously) separates things, but whitespace isn't neccessary, if the two 'words' don't share any legal characters. Thus, 5+2 is legal as is 5 + 2, and public static works, but publicstatic does not. This tokenization step occurs first and is done on limited knowledge. You could in theory surmize from context that, say, publicstatic void foo() {} can't really mean anything else, but the amount of knowledge you need to draw that conclusion is quite complicated, and tokenization just does not have it. Hence, publicstatic does not work, but 5+2 does.
Based on that rule on tokenization, int y = --3; and int y = - -3 is different for the same reason publicstatic and public static is different. -- is a single 'word', meaning: Unary decrement. You can't split it up (you can't put spaces in between the two minus signs), and 2 consecutive minus signs without spaces in between is going to be tokenized as the unary decrement operator, and not as 2 consecutive binary-minus/unary-negative words. You COULD draw the conclusion that int y = --3; only has one non-stupid interpretation (2 minus signs), because the other obvious interpretation (unary decrement the expression '3') is a compiler error: You can't decrement a constant. But, that goes back to the earlier rule: If you have to take into account all complications at all times, parsing java source files is incredibly complicated, and for no meaningful gain: You really do not want to write source code where things are interpreted correctly or not based on exactly how intelligent the compiler ended up being. It aint english, you want consistency and clarity at all times. Poetic license is not a good thing, when talking directly to computers.
CONCLUSION: --3 does not work. It never can. Whomever informed you just messed up, or you misread it, and they were talking about - -3 and you didn't notice the space, or it got lost in translation.
Are there cases where --expr can be evaluated as double negation instead of decrement?
Not like that, no. - -expr will, -(-expr) will, but -- written just like that, no spaces, no parens, nothing in between? No. Because of the tokenizer.
Why can't values suffice where the unexpected type errors were thrown?
Because there'd be absolutely no point to this, and makes javac (the part that turns your bag-o-characters into a tree structure and from there, into a class file) a few orders of magnitude more complicated. It's a computer, not english. You don't want 'best effort', and the few languages that do this (javascript, PHP) are incredibly stupid languages, universally derided, for trying (these languages are still popular, but that's despite the property they try their best instead of just having a clear spec and failing when the programmer fails to adhere to it - you'll find plenty of talks for such languages, by fans of it, making fun of the corner cases. In javascript, there's the famous WAT talk. For java there is the java puzzlers book. Both talks/books designed to teach something by showing off how you can use the language to write idiotic code that is nevertheless hard to read. Written by fans of these languages. It's a bit much to try to give you some sort of proof that you do not want a language to take its best wild stab in the dark at what you meant, but hopefully a few popular books and talks will go some way in making you realize it works like this.
How did Java evaluate d---t? Also, in what order?
Now we're getting into specifics. Java's tokenizer is such that it will tokenize that as d -- - t or d - -- t, and that's because --- is not a known word, and the java tokenizer is based on splitting things up by applying a list of known symbols and keywords to the job.
So which one does it tokenize to? It doesn't matter. Why do you want to know? So that you can write it? Don't - what possible purpose does that serve? If you write intentionally obfuscated code, you will be fired, and it'll be justified. If you are trying to train yourself to find that code perfectly readable, that's great! But less than 1% of your average java coder, even expert ones, do this, so now you've written code nobody can read, and you find code others wrote aggravating because nobody writes it like you do. So, you're unemployable as a programmer / you can't employ anybody to help you. You can build an entire career writing software on your own for the rest of your life, but it's quite limiting. Why bother?
It gets tokenized into d - -- t or d -- - t.
If you find this sort of thing entertaining (I certainly do!), then know full well this is no more useful than playing a silly game, there is no academic or career point to it, at all.
The problem is, you don't seem to find it entertaining. If you did, you'd have done a trivial experiment to find out. If it's d -- - t (subtract X and Y, where X is d, post-unary-decrement, and Y is t), then after all that, d will be one smaller, which you can trivially test for. If it's d - --t, then t would be one smaller afterwards. If it's d - -(-t) (as in, d minus Z, where Z is negative Y, where Y is negative t), then neither will have changed. You didn't do this experiment. That eliminates 'you find it fun'. I've eliminated 'this is useful', which leaves us with: This question does not matter, therefore neither does the answer.
There is a small chance this question shows up in a curriculum of some sort. If it does, do yourself a giant favour and find another curriculum. It's an incredibly strong indicator of extremely low quality java teaching, if you expect your students to know how d---t breaks down.
You talk about 'reasoning', but reasoning has no place here. d -- - t, d - -- t and d - -(-t) are all equally valid interpretations, reason isn't going to tell you which one is correct. The language designers threw some dice to decide. They knew it didn't matter (Except for the last one, which isn't possible without a 'smart' tokenizer, and you don't want one of those, but the reasoning needed to draw the conclusion that you need a smart tokenizer, let alone the reasoning needed to draw the conclusion that smart tokenizers are a bad idea, is incredibly complicated and requires a ton of experience writing parsers from scratch, or possibly a mere full year's worth of parser language courses at a uni level might give you the wherewithal to figure that one out - I think you can be excused for not picking up that detail :P).

Bitwise operator advantages in StringBuilder

Why does the reverse() method in StringBuffer/StringBuilder classes use bitwise operator?
I would like to know the advantages of it.
public AbstractStringBuilder reverse() {
boolean hasSurrogate = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; --j) {
char temp = value[j];
char temp2 = value[n - j];
if (!hasSurrogate) {
hasSurrogate = (temp >= Character.MIN_SURROGATE && temp <= Character.MAX_SURROGATE)
|| (temp2 >= Character.MIN_SURROGATE && temp2 <= Character.MAX_SURROGATE);
}
value[j] = temp2;
value[n - j] = temp;
}
if (hasSurrogate) {
// Reverse back all valid surrogate pairs
for (int i = 0; i < count - 1; i++) {
char c2 = value[i];
if (Character.isLowSurrogate(c2)) {
char c1 = value[i + 1];
if (Character.isHighSurrogate(c1)) {
value[i++] = c1;
value[i] = c2;
}
}
}
}
return this;
}
Right shifting by one means dividing by two, I don't think you'll notice any performance difference, the compiler will perform these optimization at compile time.
Many programmers are used to right shift by two when dividing instead of writing / 2, it's a matter of style, or maybe one day it was really more efficient to right shift instead of actually dividing by writing / 2, (prior to optimizations). Compilers know how to optimize things like that, I wouldn't waste my time by trying to write things that might be unclear to other programmers (unless they really make difference). Anyway, the loop is equivalent to:
int n = count - 1;
for (int j = (n-1) / 2; j >= 0; --j)
As #MarkoTopolnik mentioned in his comment, JDK was written without considering any optimization at all, this might explain why they explicitly right shifted the number by one instead of explicitly dividing it, if they considered the maximum power of the optimization, they would probably have wrote / 2.
Just in case you're wondering why they are equivalent, the best explanation is by example, consider the number 32. Assuming 8 bits, its binary representation is:
00100000
right shift it by one:
00010000
which has the value 16 (1 * 24)
In summary:
The >> operator in Java is known as the Sign Extended Right Bit Shift operator.
X >> 1 is mathematically equivalent to X / 2, for all strictly positive value of X.
X >> 1 is always faster than X / 2, in a ratio of roughly 1:16, though the difference might turn out to be much less significant in actual benchmark due to modern processor architecture.
All mainstream JVMs can correctly perform such optimizations, but the non-optimized byte code will be executed in interpreted mode thousand of times before these optimization actually occurs.
The JRE source code use a lot of optimization idioms, because they make an important difference on code executed in interpreted mode (and most importantly, at the JVM launch time).
The systematic use of proven-to-be-effective code optimization idioms that are accepted by a whole development team is not premature optimization.
Long answer
The following discussion try to correctly address all questions and doubts that have been issued in other comments on this page. It is so long because I felt that it was necesary to put emphasis on why some approach are better, rather than show off personal benchmark results, beliefs and practice, where millage might significantly vary from one person to the next.
So let's take questions one at a time.
1. What means X >> 1 (or X << 1, or X >>> 1) in Java?
The >>, << and >>> are collectively known as the Bit Shift operators. >> is commonly known as Sign Extended Right Bit Shift, or Arithmetic Right Bit Shift. >>> is the Non-Sign Extended Right Bit Shift (also known as Logical Right Bit Shift), and << is simply the Left Bit Shift (sign extension does not apply in that direction, so there is no need for logical and arithmetic variants).
Bit Shift operators are available (though with varying notation) in many programming language (actually, from a quick survey I would say, almost every languages that are more or less descendents of the C language, plus a few others). Bit Shifts are fundamental binary operations, and consquently, almost every CPU ever created offer assembly instructions for these. Bit Shifters are also a classic buiding block in electronic design, which, given a reasonable number of transitors, provide its final result in a single step, with a constant and predicatable stabilization period time.
Concretly, a bit shift operator transforms a number by moving all of its bits by n positions, either left or right. Bits that falls out are forgotten; bits that "comes in" are forced to 0, except in the case of the sign extended right bit shift, in which the left-most bit preserve its value (and therefore its sign). See Wikipedia for some graphic of this.
2. Does X >> 1 equals to X / 2?
Yes, as long as the dividend is guaranteed to be positive.
More generally:
a left shift by N is equivalent to a multiplication by 2N;
a logical right shift by N is equivalent to an unsigned integer division by 2N;
an arithmetic right shift by N is equivalent to a non-integer division by 2N, rounded to integer toward negative infinity (which is also equivalent to a signed integer division by 2N for any strictly positive integer).
3. Is bit shifting faster than the equivalent artihemtic operation, at the CPU level?
Yes, it is.
First of all, we can easily assert that, at the CPU's level, bit shifting does require less work than the equivalent arithmetic operation. This is true both for multiplications and divisions, and the reason for this is simple: both integer multiplication and integer division circuitry themselves contains several bit shifters. Put otherwise: a bit shift unit represents a mere fraction of the complexity level of a multiplication or division unit. It is therefore guaranteed that less energy is required to perform a simple bit shift rather than a full arithmetic operation. Yet, in the end, unless you monitor your CPU's electric consumption or heat dissipation, I doubt that you might notice the fact that your CPU is using more energy.
Now, lets talk about speed. On processors with reasonnably simple architecture (that is roughly, any processor designed before the Pentium or the PowerPC, plus most recent processors that do not feature some form of execution pipelines), integer division (and multiplication, to a lesser degree) is generally implemented by iterating over bits (actually group of bits, known as radix) on one of the operand. Each iteration require one CPU cycle, which means that integer division on a 32 bits processor would require (at most) 16 cycles (assuming a Radix 2 SRT division unit, on an hypothetical processor). Multiplication units usually handle more bits at once, so a 32 bits processor might complete integer multiplication in 4 to 8 cycles. These units might use some form of variable bit shifter to quickly jump over sequence of consecutive zeros, and therefore might terminate quickly when multiplying or dividing by simple operands (such as positive power of two); in that case, the arithmetic operation will complete in less cycles, but will still require more than a simple bit shift operation.
Obviously, instruction timing vary between processor designs, but the preceeding ratio (bit shift = 1, multiplication = 4, division = 16) is a reasonable approximation of actual performance of these instructions. For reference, on the Intel 486, the SHR, IMUL and IDIV instructions (for 32 bits, assuming register by a constant) required respectively 2, 13-42 and 43 cycles (see here for a list of 486 instructions with their timing).
What about CPUs found in modern computers? These processors are designed around pipeline architectures that allow the simultaneous execution of several instructions; the result is that most instructions nowaday require only one cycle of dedicated time. But this is misleading, since instructions actually remains in the pipeline for several cycles before being released, during which they might prevent other instructions from being completed. The integer multiplication or division unit remains "reserved" during that time and therefore any further division will be hold back. That is particularly a problem in short loops, where a single mutliplication or division will end up being stalled by the previous invocation of itself that hasn't yet completed. Bit shift instructions do not suffer from such risk: most "complex" processors have access to several bit shift units, and don't need to reserve them for very long (though generally at least 2 cycles for reasons intrinsic to the pipeline architecture). Actually, to put this into numbers, a quick look at the Intel Optimization Reference Manual for the Atom seems to indicates that SHR, IMUL and IDIV (same parameter as above) respectively have a 2, 5 and 57 latency cycles; for 64 bits operands, it is 8, 14 and 197 cycles. Similar latency applies to most recent Intel processors.
So, yes, bit shifting is faster than the equivalent arithmetic operations, even though in some situations, on modern processors, it might actualy makes absolutely no difference. But in most case, it is very significant.
4. Will the Java Virtual Machine will perform such optimization for me?
Sure, it will. Well... most certainly, and... eventually.
Unlike most language compilers, regular Java compilers perform no optimization. It is considered that the Java Virtual Machine is in best position to decide how to optimize a program for a specific execution context. And this indeed provide good results in practice. The JIT compiler acquire very deep understanding of the code's dynamics, and exploit this knowledge to select and apply tons of minor code transforms, in order to produce a very efficient native code.
But compiling byte code into optimized native methods require a lot of time and memory. That is why the JVM will not even consider optimizing a code block before it has been executed thousands of times. Then, even though the code block has been scheduled for optimization, it might be a long time before the compiler thread actualy process that method. And later, various conditions might cause that optimized code block to be discarded, reverting back to byte code interpretation.
Though the JSE API is designed with the objective of being implementable by various vendor, it is incorrect to claim that so is the JRE. The Oracle JRE is provided to other everyone as the reference implementation, but its usage with another JVM is discouraged (actualy, it was forbiden not so long ago, before Oracle open sourced the JRE's source code).
Optimizations in the JRE source code are the result of adopted conventions and optimization efforts among JRE developpers to provide reasonable performances even in situations where JIT optimizations haven't yet or simply can't help. For example, hundreds of classes are loaded before your main method is invoked. That early, the JIT compiler has not yet acquired sufficient information to properly optimize code. At such time, hand made optimizations makes an important difference.
5. Ain't this is premature optimization?
It is, unless there is a reason why it is not.
It is a fact of modern life that whenever a programmer demonstrate a code optimization somewhere, another programmer will oppose Donald Knuth's quote on optimization (well, was it his? who knows...) It is even perceived by many as the clear assertion by Knuth that we should never try to optimize code. Unfortunately, that is a major misunderstanding of Knuth's important contributions to computer science in the last decades: Knuth as actually authored thousand of pages of literacy on practical code optimization.
As Knuth put it:
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
— Donald E. Knuth, "Structured Programming with Goto Statements"
What Knuth qualify as premature optimization are optimizations that require lot of thinking and apply only to non critical part of a program, and have strong negative impact on debugging and maintenance. Now, all of this could be debated for a long time, but let's not.
It should however be understood that small local optimizations, that have been proven to be effective (that is, at least in average, on the overall), that do not negatively affect the overall construction of a program, do not reduce a code's maintainability, and do not require extraneous thinking are not a bad thing at all. Such optimizations are actualy good, since they cost you nothing, and we should not pass up such opportunities.
Yet, and that is the most important thing to remember, an optimization that would be trivial to programers in one context might turn out to be incomprenhendable to programmers in another context. Bit shifting and masking idioms are particularly problematic for that reason. Programmers that do know the idiom can read it and use it without much thinking, and the effectiveness of these optimizations is proven, though generaly insignificant unless the code contains hundreds of occurences. These idioms are rarely an actual source of bugs. Still, programmers unfamilliar with a specific idiom will loose time understanding what, why and how that specific code snippet does.
In the end, either to favor such optimization or not, and exactly which idioms should be used is really a matter of team decision and code context. I personnaly consider a certain number of idioms to be best practice in all situations, and any new programmer joining my team quickly acquire these. Many more idioms are reserved to critical code path. All code put into internal shared code library are treated as critical code path, since they might turns out to be invoked from such critical code path. Anyway, that is my personal practice, and your millage may vary.
It uses (n-1) >> 1 instead of (n-1)/2 to find the middle index of the internal array to be reversed. Bitwise shift operators are usually more efficient than the division operator.
In this method, there's just this expression: (n-1) >> 1. I assume this is the expression you're referring to. This is called right shift/shifting. It is equivalent to (n-1)/2 but it's generally considered faster, more efficient. It's used often in many other languages too (e.g. in C/C++).
Note though that modern compilers will optimize your code anyway even if you use division like (n-1)/2. So there are no obvious benefits of using right shifting. It's more like a question of coding preference, style, habit.
See also:
Is shifting bits faster than multiplying and dividing in Java? .NET?

How do comparison operators work in java?

Recently someone told me that a comparison involving smaller integers will be faster, e.g.,
// Case 1
int i = 0;
int j = 1000;
if(i<j) {//do something}
// Case 2
int j = 6000000;
int i = 500000;
if(i<j){//do something else}
so, comparison (if condition) in Case 1 should be faster than that in Case 2. In Case 2 the integers will take more space to store but that can affect the comparison, I am not sure.
Edit 1: I was thinking of binary representation of i & j, e.g., for i=0, it will be 0 and for j=1000 its 1111101000 (in 32-bit presentation it should be: 22 zeros followed by 1111101000, completely forgot about 32-bit or 64-bit representation, my bad!)
I tried to look at JVM Specification and bytecode of a simple comparison program, nothing made much sense to me. Now the question is how does comparison work for numbers in java? I guess that will also answer why (or why not) any of the cases will be faster.
Edit 2: I am just looking for a detailed explanation, I am not really worried about micro optimizations
If you care about performance, you really only need to consider what happens when the code is compiled to native code. In this case, it is the behaviour of the CPU which matters. For simple operations almost all CPUs work the same way.
so, comparison (if condition) in Case 1 should be faster than that in Case 2. In Case 2 the integers will take more space to store but that can affect the comparison, I am not sure.
An int is always 32-bit in Java. This means it always takes the same space. In any case, the size of the data type is not very important e.g. short and byte are often slower because the native word size is 32-bit and/or 64-bit and it has to extract the right data from a larger type and sign extend it.
I tried to look at JVM Specification and bytecode of a simple comparison program, nothing made much sense to me.
The JLS specifies behaviour, not performance characteristics.
Now the question is how does comparison work for numbers in java?
It works the same way it does in just about every other compiled language. It uses a single machine code instruction to compare the two values and another to perform a condition branch as required.
I guess that will also answer why (or why not) any of the cases will be faster.
Most modern CPUs use branch prediction. To keep the CPUs pipeline full it attempt to predict which branch will be taken (before it know it is the right one to take) so there is no break in the instruction executed. WHen this works well the branch has almost no cost. When it mis-predicts, the pipeline can be filled with instructions for a branch which was the wrong guess and this can cause a significant delay as it clears the pipeline and takes the right branch. In the worst case it can mean 100s of clock cycles delay. e.g.
Consider the following.
int i; // happens to be 0
if (i > 0) {
int j = 100 / i;
Say the branch is usually taken. This means the pipeline could be loaded with an instruction which triggers an interrupt (or Error in Java) before it knows the branch will not be taken. This can result in a complex situation which takes a while to unwind correctly.
These are called Mispredicted branches In short a branch which goes the same way every/most of the time is faster, a branch which suddenly changes or is quite random (e.g. in sorting and tree data structures) will be slower.
Int might be faster on 32-bit system and long might be faster on 64-bit system.
Should I bother about it? No you dont code for system configuration you code based on what are your requirements. Micro optinizations never work and they might introduce some unprecedented issues.
Small Integer objects can be faster because java treats them specific.
All Integer values for -128 && i <= 127 are stored in IntegerCache an inner class of Integer

Other than readability, is there any value add if I use += operator like x+=y instead of x=x+y in java

I do understand that both += and var1=var1+var2 both do the same thing in Java. My question is, apart from better readability, what other benefit(if any) the former brings? Im asking because I was curious why java developers introduced this, they should have done it because of some value addition.
It prevents the LHS of the + operation from being evaluated twice.
var.getSomething().x = var.getSomething().x + y;
This is not equivalent to
var.getSomething().x += y;
However, the added value from increased readability and reduced typing effort is not to be underestimated.
x+=y and x=x+y are not same.
x+=y is equivalent to x=(Type of x)(x+y)
byte x=4;
byte y=3;
x+=y; // x =(byte)(x+y)
x = x + y; // compile time error
IMHO these operators (+=, -=, *=, /=) are inherited from C. In the old days (before we had highly optimising compilers), they could improve performance because on many architectures, these operators translated directly into a single assembler instruction. Nowadays, all compilers should automatically produce the same code. As for Java, I don't know if the client VM will produce the same bytecode even if there are no side effects on the RHS. Shouldn't make a difference in performance though.
And Dietrich is right, += et all prevent the argument from being evaluated twice (important when there are side effects).

Comparing c and java programs runtime

I had a job interview today, we were given a programming question, and were asked to solve it using c/c++/Java, I solved it in java and its runtime was 3 sec (the test was more 16000 lines, and the person accompanying us said the running time was reasonable), another person there solved it in c and the runtime was 0.25 sec, so I was wondering, is a factor of 12 normal?
Edit:
As I said, I don't think there was really much room for algorithm variation except maybe in one little thing, anyway, there was this protocol that we had to implement:
A (client) and B (server) communicate according to some protocol p, before the messages are delivered their validity is checked, the protocol is defined by its state and the text messages that can be sent when it is in a certain state, in all states there was only one valid message that could be sent, except in one state where there was like 10 messages that can be sent, there are 5 states and the states transition is defined by the protocol too.
so what I did with the state from which 10 different messages can be sent was storing their string value in an ArrayList container, then when I needed to check the message validity in the corresponding state i checked if arrayList.contains(sentMessageStr); I would think that this operation's complexity is O(n) although I think java has some built-in optimization for this operation, although now that I am thinking about it, maybe I should've used a HashSet container.I suppose the c implementation would have been storing those predefined legal strings lexicographically in an array and implementing a binary search function.
thanks
I would guess that it's likely the jvm took a significant portion of that 3 seconds just to load. Try running your java version on the same machine 5 times in a row. Or try running both on a dataset 500 times as large. I suspect you'll see a significant constant latency for the Java version that will become insignificant when runtimes go into the minutes.
Sounds more like a case of insufficient samples and unequal implementations (and possibly unequal test beds).
One of the first rules in measurement is to establish enough samples and obtain the mean of the samples for comparison. Even a couple of runs of the same program is not sufficient. You need to tax the machine enough to obtain samples whose values can be compared. That's why test-beds need to be warmed up, so that there are little or no variables at play, except for the system under observation.
And of course, you also have different people implementing the same requirement/algorithm in different manners. It counts. Period. Unless the algorithm implementations have been "normalized", obtaining samples and comparing them are the same as comparing apples and watermelons.
I don't think I need to expand on the fact that the testbeds could have been of varying configurations, or under varying loads.
It's almost impossible to say without seeing the code - you may have a better algorithm for example that scales up much better for larger input but has a greater overhead for small input sizes.
Having said that, this kind of 12x difference is roughly what I would expect if you coded the solution using "higher level" constructs such as ArrayLists / boxed objects and the C solution was basically using optimised, low level pointer arithmetic into a pre-allocated memory region.
I'd rather maintain the higher level solution, but there are times when only hand-optimised low level code will do.....
Another potential explanation is that the JIT had not yet warmed up on your code. In general, you need to reach "steady state" (typically a few thousand iterations of every code path) before you will see top performance in JIT-compiled code.
Performance depends on implementation. Without knowing exactly what you code and what your competitor did, it's very difficult to tell exactly what happened.
But let's say for isntance, that you used objects like vectors or whatever to solve the problem and the C guy used arrays[], his implementation is going to be faster than yours for sure.
C code can be translated very efficiently into assembly instructions, while Java on the other hand, relies on a bunch of stuff (like the JVM) that might make the bytecode of your program fatter and probably a little bit slower.
You will be hard pressed to find something that can execute faster in Java than in C. Its true that an order of magnitude is a big difference but in general C is more performant.
On the other hand you can produce a solution to any given problem much quicker in Java (especially taking into account the richness of the libraries).
So at the end of the day, if there is a choice at all, it comes down as a dilemma between performance and productivity.
That depends on the algorithm. Java is of course generally slower than C/C++ as it's a virtual machine but for most common applications its speed is sufficient. I would not call a factor of 12 normal for common applications.
Would be nice if you posted the C and Java codes for comparison.
A factor of 12 can be normal. So could a factor of 1 or 1/2. As some commentators mentioned, a lot has to do with how you coded your solution.
Dont forget that java programs have to run in a jvm (unless you compile to native machine code), so any benchmarks should take that into account.
You can google for 'java and c speed comparisons' for some analysis
Back in the days I'd say that there's nothing wrong with your Java code being 12 times slower. But nowadays I'd rather say that the C guy implemented it more efficiently. Obviously I might be wrong, but are you sure you used proper data structures and well simply coded it well?
Also did you measure the memory usage? This might sound silly, but last year at the uni we had a programming challenge, don't really remember what it was but we had to solve a graph problem in whatever language we wanted - I did two implementations of my algorithm one in C and one in Java, the Java one was ~1,5-2x slower, BUT for instance I knew I didn't have to worry about memory management (I knew exactly how big the input will be and how many test samples will be run from the teacher) so I simply didn't free any memory (which took way too much time in a programme that run for ~1-2seconds on a graph with ~15k nodes, or was it 150k?) so my Java code was better memory wise but it was slower. I also parsed the input myself in C (didn't do that in Java) which saved me really A LOT of time (~20-25% boost, I was amazed myself). I'd say 1,5-2x is more realistic than 12x.
Most likely the algorithm used in the implementation was different.
For instance ( an over simplification ) if you want to add a number N , M number of times one implementation could be:
long addTimes( long n, long m ) {
long r = 0;
long i;
for( i = 0; i < m ; i++ ) {
r += n;
}
return r;
}
And another implementation could simply be:
long addTimes( long n, long m ) {
return n * m;
}
Both, will run mostly the same in Java and C (you don't even have to change the code ) and still, one implementation will run way lot faster than the other.

Categories