Sunday, August 6, 2017

Operators and Expressions

Expressions form the basis of what we do in Java. While it is good to understand how variables and literals work, we cannot do very much with them on their own. By combining variables and literals with operators, we can create powerful expressions to perform more advanced computations. An expression can be thought of as a series of variables, literals, and operators that return a single value. Here are some examples:

3 //simple literal expression that results in a value of 3
3 + 4 //arithmetic expression resulting in a value of 7
(3 + 4) * 2 //more advanced arithmetic expression using order of operation, resulting in a value of 14
6 & 5 //bitwise expression resulting in a value of 4
3 >= 4 //relational expression resulting in a value of false
8 > 10 ? 15 < 20 ? 50 : 80 : 3 >= 2 ? 4 : 8; //very complicated expression using the ternary operator, resulting in a value of 4. Not recommended to use complicated expressions because it is difficult to read/modify


Sometimes people refer to expressions in java as operations. An operation in Java is similar to a mathematical operation - it modifies a value (or multiple values) to produce a new value. An operand is the variable or literal that is used in the operation. An operator is a symbol that is applied to the operand(s). This is best explained in an example:

int daysInAYear = 365;
int daysInThreeYears = daysInAYear * 3;
int daysInFiveYears = daysInThreeYears + daysInAYear * 2;
daysInAYear++; //leap year calculation
System.out.println(daysInAYear); //366

The first line is a simple assignment operation that takes the literal value 365 and copies it to the variable daysInAYear, which is of type integer. In this case, 365 is the operand, the = is the assignment operator that copies the value over, and daysInAYear is the result of the assignment operation (365).

The second line should be fairly straightforward. It takes daysInAYear and multiplies it by 3 to produce a new value daysInThreeYears. The variable daysInAYear and the literal 3 are the operands, and the * and = are the operators. What's interesting here is that the multiplication operator * takes two operands (daysInAYear and 3) while the assignment operator = only takes one (the result of daysInAYear * 3 = 1095). In Java, some operators take two operands and some only take one. Additionally, there is a ternary operator which takes 3 operands. This will be discussed later when we get to control statements.

The third line takes daysInThreeYears and 2 times daysInAYear to derive a new value which is assigned to daysInFiveYears. There are three operators +, *, and =. Similar to mathematics, the order in which these operators are applied is important. If daysInThreeYears + daysInAYear were calculated first, and then multiplied by 2, then the value returned would be the number of days in eight years (4 * 2). But the multiplication * operator has precedence over the + operator, so daysInAYear is first multiplied by 2 and then the result is added to daysInThreeYears, which results in the number of days in five years. Finally, the end result is assigned to the variable daysInFiveYears.

To recap, multiplication (*) has precedence over addition (+), which has precedence over assignment (=). This becomes more important as the calculations get more complex. Java provides parenthesis () to increase the precedence of the operation inside of it. For example, (daysInThreeYears + daysInAYear) * 2 would result in 8 * 365 = 2920, since the + is evaluated first, and then that result (4 * 365) is multiplied by 2. And daysInThreeYears + (daysInAYear * 2) would produce 5 * 365 = 1825, which is the same as the original expression daysInThreeYears + daysInAYear * 2. In this case, the parenthesis are unnecessary since * already has precedence over +. However, it is a good practice to include parenthesis in cases where other programmers might have a hard time figuring out what the order is. It's probably not needed in this simple example, but as operations get more complex, it really helps to see visually which parts of the operation are evaluated first. It's also a good idea to avoid overly complicated expressions and break them up in to multiple lines if needed. For example:

int daysInTwoYears = daysInAYear * 2;
int daysInFiveYears = daysInTwoYears + daysInThreeYears; //No confusion here, it's just a simple additon statement

In line 4, we come across a new operator ++. This is called the increment operator, and in this case it's used to increment the value of the variable daysInAYear by 1 and assign it back to itself. The result of this operation is 366, as shown on line 5. The increment operator commonly used in loops, which will be discussed later on. We could have also written daysInAYear = daysInAYear + 1;, but ++ is a convenient shortcut to do the same thing. There is also a decrement operator -- which is the same as the increment operator, except it subtracts 1 instead of adding 1. It is illegal to apply the increment or decrement operator to a literal. For example:

int daysInALeapYear = 365++; //error, ++ should be applied to a variable instead of being applied directly to the literal 365

Operator Types

Now that we've seen some examples of operators in Java, let's take a more detailed look at the different groups of operators and what's available for us to use. There are four basic groups of operators: arithmetic, bitwise, relational, and logical. These are discussed in detail below:

Arithmetic operators

Arithmetic operators are used to perform basic mathematical computations with numbers (integers, doubles, etc). Here is the list of arithmetic operators, along with their description:

Operator Description
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulus
+= Addition Assignment
-= Subtraction Assignment
*= Multiplication Assignment
/= Division Assignment
%= Modulus Assignment
++ Increment
-- Decrement

Most of these should look pretty familiar. The +, -, *, and / do what we'd expect. One thing to note is that dividing two integers will produce another integer and truncate (discard) the fractional part. For example, 8/3 would produce 2.6666667 on a standard calculator, but in Java, the fractional part is discarded and we are left with 2.

The % operator is known as the modulus operator and it is the remainder when the first number is divided by the second. For example 11/3 is 3 with a remainder of 2, so 11 % 3 = 2. And 12/3 is 4 with a remainder of 0, so 12 % 3 = 0. The modulus operator is commonly used to repeat a series of numbers. If I start with 0 and continually increment it by 1, and mod the result by 2, the answer will alternate between 0 and 1:

0 % 2 = 0
1 % 2 = 1
2 % 2 = 1
3 % 2 = 1
4 % 2 = 0
...

This could be useful when creating a table with alternating row colors. Another use could be time conversion. 10000 seconds is two hours (7200) with a remainder of 2800 seconds, which is equal to 46 minutes with a remainder of 40 seconds. It's worth noting that modulus can be performed on both positive and negative numbers. In that case, the sign of the result is the same as the sign of the first number:

-8 % 3; //-2, 8 % 3 = 2, but the first number is negative
-8 % -3; //also -2, since the first number is still negative
8 % -3; //2, since the first number is positive

I haven't come accross any uses of using % with negative numbers in real world projects, but it's worth noting in case you encounter that in other people's code.

The arithmentic assignment operators (+=, -=, *=, /=, and %=) behave similarly to +, -, *, /, and %, except that the result is assigned to the variable at the same time. For example, we could write something like:

int a = 4;
a = a + 2

but int a = 4; a += 2 is a convenient shorthand. The line a += 2 simply takes the value that was already to a, adds 2, and assigns the result back to a. These operators are also known as compound operators, because they do multiple things (perform arithmentic and assign the result) using a single operator.

The last two operators (++ and --) are a shorthand for the compound assignment operators. Specifically, ++ adds 1 and assigns the result back to the variable, and -- subtracts 1 and assigns the result back. Here is an example:

int x = 2;
x++; //x is now 3
x--; //x is now 2
x--; //x is now 1
System.out.println(x); //1

A common use of ++ and -- is to repeatedly increment or decrement a value by 1 in a loop. This will be covered in detail in a later post.

Bitwise operators

Bitwise operators can be applied to whole number types (long, int, short, byte, or even char), but they act upon the individual bits of a number instead of the numbers themselves. In order to understand bitwise operators, we must convert the numbers to binary form, as in these examples:

32 = 2^5 or 100000
47 = 2^5 + 2^3 + 2^2 + 2^1 + 2^0 or 101111
48 = 2^5 + 2^4 or 110000

If you're not familiar with converting numbers between decimal and binary, you'll be happy to know that the bitwise operators don't see a lot of use in actual Java programs. Nevertheless, let's take a look at the bitwise operators provided by the Java language:

Operator                Description
& Bitwise and
| Bitwise or
~ Bitwise not (negation)
^ Bitwise exclusive or
&=                      Bitwise and assignment
|=                      Bitwise or assignment
^=                      Bitwise exclusive or assignment
>>                      Right shift
<<                      Left shift
>>=                     Right shift assignment
<<=                     Left shift assignment
>>>                     Right shift zero fill
>>>=                    Right shift zero fill assignment

Many of the bitwise operators don't see much usage, and it's a bit early in this blog to introduce all of them. For now, let's take a look at how the bitwise and/or works. Bitwise and converts the numbers to binary (base 2) notation and 'ANDs' the bits together from left to right. In other words, if both bits have a value of 1, the result is 1. If at least 1 of the bits is 0, the result is 0. Bitwise 'OR' works similarly, except if either of the two bits is 1 (or both bits are 1), then the result is 1. If both bits are 0, then the result is 0. Let's take a look at an example:

9 & 5

Step 1: convert 9 and 5 to binary

9 = 1001
5 = 0101

Step 2: 'AND' the bits together from left to right

0001 (Starting from the left, 1 & 0 = 0, 0 & 1 = 0, 0 & 0 = 0, 1 & 1 = 1)

Step 3: Convert the results back to base 10

Answer: 9 & 5 = 1

-----

And here's an example for bitwise OR:

9 | 5

Step 1: convert 9 and 5 to binary

9 = 1001
5 = 0101

Step 2: 'OR' the bits together from left to right

1101 (Starting from the left, 1 | 0 = 1, 0 | 1 = 1, 0 | 0 = 0, 1 | 1 = 1)

Step 3: Convert the results back to base 10

1101 = 8 + 4 + 1 = 13

Answer: 9 | 5 = 13

Not too bad, huh? Again, bitwise operators don't see much use in actual code. Some examples are compression, encryption, and low-level computer graphics operations. Let's move on to relational operators.

Relational Operators

Relational operators compare the value on the left with the value on the right and produce a boolean value (true or false). Here is the list of relational operators in Java:

Operator Description
==                      Equal to
!=                      Not equal to
>                       Greater than
<                       Less than
>=                      Greater than or equal to
<=                      Less than or equal to

These operators work in almost the same way as they do in math. One thing to note is that == and != can be applied booleans, while the other relational operators only apply to numbers. Here are some examples:

4 == 5 //false
4 != 5 //true
4 != 4 //false
5.5 > 3.2 //true
5.5 < 3.2 //false
5.5 >= 4 //true
5.5 >= 5.5 //true
5.5 <= 4 //false
'c' > 'a' //true, characters are internally stored as numbers in Java, so this is allowed. Letters later in the alphabet are considered greater than earlier letters
'c' == 'a' //false
true == false //false
true > false //error, > can only be applied to numbers

Relational operators are commonly used in conditional statements, which will be covered in the next section, so we'll get a lot more practice with these. Unlike the bitwise operators, relational operators are frequently used, and you can expect to see them in virtually every Java program.

Ternary Operator

The ternary operator (? symbol) is an alternative way to write certain types of conditional statments. It will be looked at in the next section.

Example

Now that you've seen the four types of operators (arithmetic, bitwise, relational, ternary), it's time to write a small program to test things out. This program will compute the area of a circle given it's radius. In order to make things a bit more interactive, I used a Scanner to read the user's input from the console. This is similar to the System.out.println() method we saw in an earlier post, except the scanner reads from the console instead of writing to it. Here is the full program:

import java.util.Scanner;

public class ExpressionsTest {
public static void main(String[] args) {
System.out.println("This program computes the area of a circle given the radius.");
System.out.print("Enter the radius of the circle in centimeters: ");
Scanner scanner = new Scanner(System.in);
double circleRadius = scanner.nextDouble();
double circleArea = circleRadius * 2 * Math.PI;
System.out.println("The area of the circle is " + circleArea + " centimeters");
scanner.close();
}
}

Apart from the code which creates the scanner and reads the input, most of this code should be straightforward. The line "double circleArea = circleRadius * 2 * Math.PI" is our arithmetic expression to convert the circle radius into an area. Math.PI is a built-in costant in the Java language that provides an approximation of pi. After printing out the area of the circle to the console, we close the scanner to avoid any resource leakage (this will be covered in more detail later). Here is a screenshot of the program in action:



With most of the basic expressions covered, it's time to move on to conditional statements and enhance our basic example.

1 comment: