Arrays and Operators   latest revision Aug 1 / 2001     Peter Komisar

 referemces: 'The Java1.1 Study Certification Guide', Heller&Roberts 'Just Java' , P.V. Linden



Arrays

An array is an ordered collection of  primitives, objects or other arrays. In Java there
are three common, non-primitive, object reference types, classes, interfaces and arrays.

Arrays are like classes in that they are

1) assigned to references,
2) are allocated with the new operator and
3) have Object class as a parent.

Arrays are not like classes in that they

1) can't be subclassed
2) they have their own syntax and
3) can't have custom methods defined.

Further,

1) a given array's elements are all of the same type.
2) Java array indexes start at 0.
3) Arrays can be passed as arguments.
4) An array's length is available through the final, data field, length.

//an array's  length  field is easy to confuse with the String class method length( )

Array Declaration

int I [ ];     // is the same as
int [ ] I ;     // and declares an array of integers

double _2D [ ][ ];      // is the same as
double[ ][ ] _2D;
or double [ ] _2D [ ] ;   //and declares an array of an arrays of doubles
 
 
More on Array Declarations

int [] i, j:  // is the same as declaring the two arrays seperately as follows
int[] i;
int[]j; 


int [] k, l[], m[][];  // is the same as declaring the following arrays
int []k;
int [][]l;
int [][][]m; 

// note how the first part of the multiple array declaration (int []) , distributes 
// across whatever is adjoined to the subsequent variables
 


 

Construction

Like a class, size is allocated at construction via the new keyword at runtime.

int I [];               // declaration
I = new int [15];    // instantiation (or construction) is coupled with setting the initial size of an array

int size = 20;
I = new int [size];     // size can also be set via a variable

String[] s = new String [99];   // It is common for declaration & construction to appear on the same line

When first constructed, array elements are initialized to the default values for the type the
array contains. For example, an int array will have each of it's elements set by default to 0.
An array of type double will have each of it's elements set by default to 0.0 .
 

Inititialization

1) Arrays can be initialized with a set of like types matching the type of the array. For example

    String[ ]   s = { "one", "two", "three" };   // or
    double[ ] d = { 1.1, 2.2, 3.3, 4.4, 5.5 };

2) Arrays can also be initialized by explicit assignment as in the following example.

    short [ ] cubes = new short[25];
    for( int i =0; i < cubes.length; i ++)
    cubes[i] = i * i * i;

3) JDK 1.1 introduced an array creation expression with the form

new types[] { values. . . };

Example byte b[ ] = new byte[ ] { 5, 5, 5 };    // an array expression statement used in an assignment
 



A couple methods that work on arrays

1) The clone( ) method can be used to make an exact copy of an array. The clone( ) method
    returns type Object. When using this method the return value is cast back to the array type..
    For example,

    int Z [] = new int []{1,9,11};
    int Y [] = (int [ ] )  Z.clone( );  // Y array will be a perfect copy of Z containing 1,9, & 11

    The type in brackets, ( int [ ] ) , is a cast and, changes the method's return value back to
    an array type


2) arraycopy( ) is a System class method that can copy all or part of an array. Here is how
    it is defined.

    public static native void arraycopy
    (Object src, int src_position, Object dst, int st_position, int length);

// Following is how the operation of the method is described in the API.

Copies an array from the specified source array, beginning at the specified position, to the specified
position of the destination array .... The number of components  copied is equal to the length argument.
The components at positions srcOffset through srcOffset+length-1 in the source array are copied into
positions dstOffset through dstOffset+length-1, respectively, of the destination array.

Code Example

class arrayCopy{
   public static void main(String[] args){
     // a couple of String arrays are created by different techniques
     String[]  statement = new String[] { "It's", "not", "true", "it", "was", "my", "plan"};
     String[] out_of_context= new String[ 4];
    // the method arraycopy() is demonstrated
     System.arraycopy(statement, 3, out_of_context, 0, 4);
        for(int i=0; i <out_of_context.length; i++)
          System.out.print(out_of_context[i].toUpperCase( ) + " " );
          System.out.println("");      // just for a space
    //  the clone() method is used to duplicate the String array referenced by statement
    //  and assign the copy to the twin String array
     String[] twin= ( String[] ) statement.clone( );
       for(int i=0; i <3; i++)
         System.out.print( twin[i] + " " );
      }
}


Array of Arrays

Java describes it's compound arrays as arrays of arrays (rather than multidimensional arrays).

Chev cars [ ][ ];                                  // an array of array(s) declared but not allocated

Ford fords[ ][ ] = new Ford[9] [999]; // a declaration where fords references an array of 9 element.
                                                           // And each of these elements is an array of 999 Ford objects.

The subarrays don't have to be a single size. Here's an array creation to fill a triangle where
each subarray is a different size

int Triangle[ ][ ]= new int[ ][ ]{
                                    new int[ ]{1},                  //   or just put   {1},
                                    new int[ ]{2,2},                 //                        {2,2},
                                    new int[ ]{3,3,3},         //                         {3,3,3},
                                    new int[ ]{4,4,4,4}    //                        {4,4,4,4}
                                    };

int [ ][ ][ ] giga = new int[1000][1000][1000]; // a gigabyte array

//Van der Linden warns to watch the size the compound arrays you create
 
/*     This class shows an assignment to one of the array elements and a printout of it and 
    the following value which will have the default value for an int stored in it. The default 
   values  stored in arrays correspond to the type associated with the array. */


class a2T{
  public static void main(String[] args){
  int [ ][ ][ ] giga = new int[100][100][100];
  giga[51][52][53] =55;
  System.out.println(giga[51][52][53] +" "+ giga[51][52][54]);
  }
} // prints out 55 and zero

To return an array from a method it is suggested to use

int[ ] method( ) {
   return new int[9];
   }                              // rather than the following C style

 int method( ) [] {
   return new int [11];
  }

This supports the thought of those that puts the square braces after the type but before the
identifier. For example, main ( String [] args) vs main (String args []). The first form clearly
shows an array of type String is being returned by the method. A hasty look at the second
method form might give the impression that it returns just a String.



Expression and Statements


 

Before looking at Operators, we should define what an expression is An expression
is something that evaluates to something. If I said my age was (10 x 5 ) you would
know I really meant 50. (10 x 5 ) is an expression that evaluates to the number 50.
In Java anything that evaluate to 1) variable, 2) a value or 3) a void is an expression..
The (10 x 5 ) expression evaluates to a value, 50. An instantiatiation of a class
evaluates to a reference value that can be stored in a variable. Sometimes an
expression doesn't resolve to a value, but only to a variable. You can think of
2*x as resolving to the variable 2x. A method can return specific values but also
can return void. A method call is considered an expression. When a expression
is concluded with a semi-colon, the expression becomes a statement. The semi-
colon is not the only way an statement ends. A block of code enclosed in curly
braces is also considered a statement.

Example  class Expresso{
                  public static void main(String[] args){
                       System.out.println( "The square root of 91: " + Math.sqrt(91) );
                       // an expression that resolves to a value
                      double sqrt2=Math.sqrt(111);
                      // an expression used in a statement
                      System.out.println("The square root of 111: " + sqrt2);
                      }
                  }
Output

C:\NewCode\Expressions>java Expresso
The square root of 91: 9.539392014169456
The square root of 111: 10.535653752852738
 



Operators

Operators are the basic symbols a language uses to do rudimentary operations. An
operand is a value or variable that is acted on in someway by an operator. If the
operator works on a single operand it is called a unary operator, an example is x ++.
This is the post-increment operator and increases the value that is stored in x by one.
If an operator works on two operands at once it is often called a binary operator.
An example is * , the multiplication operator which works on two numbers as in
( int  x = 3 * 3 ; ). There is only one ternary operator in java, which works on three
operands and it gets the name the ternary operator. We look at the ternary operator
near the end of this section.

Operations are ordered according to the rules of precedence, associativity & order
of evaluation

Precedence

Precedence is the idea that certain operations will occur before others. We expect
multiplication to occur before addition.

Example 1    3 + 2 * 2  evaluates to 7  not 10

We can use round brackets to overrule the rule of precedence.

Example 2    (3 + 2) * 2  does evaluate to 10
 

Associativity

Associativity is the property that decides which operation goes first between operations
which have equal precedence. For instance, the multiplicaton and modula operators
have the same precedence. What these operators are explained shortly.

Example     4 * 5  % 10  // division and multiplication have the same precedence which goes first.

The CoffePot rule is used to decide between operators with equal precedence. The name
abreviates Code Order For Finding Evaluating Equal Precedence Operator Textstrings.
The rule states that multiplication and division are left-associative an so are evaluated from
left to right as in ( 4 * 5 ) % 10.
 

Order of Evaluation

Order of Evaluation describes the sequence in which operands are evaluated. Java is
strictly a left to right language for order of evaluation. In the following example operands
are evaluated left to right. The left operand is evaluated before the right even in an
assignment.

// Example from the Java Certification Guide, Heller&Roberts

int [] a = new  int [2];
int b =1;
a [ b] = b = 0;

Which value of b is used?
First, evaluating left to right, a[b] is evaluated so it is a reference to the array element at a[1].
Next, b is evaluated, a reference to the variable called b. The constant expression 0 is next
evaluated which is no work. Now the operations take place in order of precedence and
associativity. The value zero is assigned to variable b and then the value zero is assigned the
last element (a[1]) of the array a. (see coffee pot rule that states assignment is right associative)
 

Java Operators in order of precedence
 
 Unary   ++   --   -  +   !   ~   ( type cast )
 Arithmetic   *     /     %  -   +
 Shift   <<     >>     >>>
 Comparison    <      <=      >      >=     instanceof
 Bitwise   &     ^     | 
 Short Circuit   &&     ||
  Ternary   ? :
  Assignment    =    or of the form    op =
   ( where op is * , / , % ,+ , - ,<< , >> , >>> , , ^  ,  or  ! )

Coffee Pot rule for determining Associativity // higher # corresponds  to higher precedence
 
 left associative operators  precedence  right associative operators  precedence
 post-increment & decrement  15  ++ pre-increment & decrement   16
 *  /  %  multiplicative   12  ~ inversion (bit flip)   14
 + -   add and subtract  11  !  logical not   14 
 <<   >>   >>>   bit wise shift  10  - + arithmetic negative & positive  14
 instanceof  <  <=  >  >=  relational    9   type conversion (cast)  13
 ==   !=    equality    8  . .
 &   bitwise AND    7 . .
 ^ exclusive OR    6 . .
 |   bitwise (inclusive) OR   5 . .
 && conditional AND    4  tertiary (conditional) operator   2
   | |  conditional OR    3  assignment      = *=  /= %= += 
 -=  <<=  >>=  >>>=  &=  ^=   != 
  1


Unary Operators

The increment and decrement Operators ++ and --

Operators which modify the value of an expression by adding or subtracting 1. They come
in pre- and post- versions. The pre-increment/decrement  types carry out the operation
before they are used, say in an assignment, while the post versions result in the original
value being used or assigned before the operation of incrementation takes place.

Example  1      int start = 7;
                     int getPost = start ++;

 // in post-increment, getPost has value 7 assigned then start is incremented to 8

Example 2       int start2 = 11;
                     int getPre = ++start2;

// in pre-increment, first start2 is incremented to 12, then getPre is assigned 12
 

The Unary + and  Operators

Unary + and  - are distinct from the binary + and - which usually refer to add and subtract.
Unary + explicitly labels a numeric literal as being positive. Unary - negates an expression.

The Bitwise Inversion Operator ~

The ~ operator performs bitwise inversion on integral types. The 0s and 1s of the
binary representation are inverted; zeros become ones and ones become zeros.
For example, applying the bitwise inversion operator to the byte value 5 causes
it's binary representation, 0000 0101 to becomes 1111 1010.

The Boolean Complement Operator !

The ! operator inverts the value of a boolean expression. !true evaluates to false.

The Cast Operator  (type)

Casts can be made to explicitly change the type of an expression. Casts can be
applied to primitive values as well as reference values though there are a lot of
rules covering which casts are legal or not. For example,

double d = 1.412;
int i = (int) d;      // results in 1.412 being converted to the int value 1
 

Arithmetic Operators
 

Multiplication and Division Operators * and  /

The * and / operators perform multiplication and division on primitive numeric types including
char type. Multiplying two integer types may result in overflow, the condition where a number
is too large to be represented by the range of it's type. In this case a  meaningless value is
obtained. Dividing with integers may result in the loss of fractional information so a loss of
precision occurs. (Integer division truncates towards zero. 9/5 is 1.8 and truncates to 1.
-9/2 is -4.5 and truncates to -4 )  Examples:

int bigInt= 2147483647;            // the biggest int before overflow
int lever= 1;
int  tooBig = bigInt + lever;   // adding one overflows to the largest negative int value, -2147483648

int x = 9;
int y = 5;
int z = x / y;                       // case of integer  imprecision shows the value of 9/5 being equal to 1

If forced to do an operation that involves both a multiplication that will go out of range and lose
meaning and a division that will lose precision, it is better to divide and lose  presision first and
then to multiply, staying in range, as you are at least left with at value that has some meaning.
 

The Modulo Operator  %

Also called the remainder operator,  returns the remainder of a division. It is usually
used with integer values but may also by used with floating point types. Example:

int h = -7 % 2;   // h is assigned the value -1
 
More on the Modulo Operator // from The Java Certification Guide, Heller&Roberts 


 A rule of thumb for calculating modulo results from negative numbers: Drop the negative signs 
 from either operand. Calculate the result. If the original left-hand operand was negative, negate 
 the result. The sign of the right hand operand is irrelevant. As with integer types a division by 
 zero involving a modulo operator can throw an Arithmetic Exception.

The Addition and Subtraction Operators + and  -

+ and - operators perform addition and subtraction. The addition operator + is overloaded
to do String concatenation.  Examples:

int j = 1;
int k = 2;
int l = j + k;
System.out.println ("One plus two is " + l + "." +
                             "The numeric literal " + (1+"") +
                             ", here is promoted to String type");

Shift Operators <<   >>   >>>

Shift operators work on the the binary form of integral type numbers, shifting the bit pattern left
or right. Because of arithmetic promotion, where integral types lower than int get promoted
to int automatically before an operation is started, shift operations should generally just be used
on int and long types. Also shifts shouldn't exceed the number of bits in the type being shifted.

// above a shift of 32 for an int the value 'rotates'. i.e  A shift of 2 on an int is the same as a shift of 34

>>  The signed right shift operator moves the bit pattern right, filling the top most bit
       position with the value that was there before the shift. For example, the number 8,
       represented as an int in binary form is

        | 0000 0000  |  0000 0000  |  0000 0000  | 0000 1000   |   // bars are just for our view

        int v = 8 >> 2;

        The above statement results in the pattern being right shift two places. The right most
        two ones are lost and the top two bit, at the left are replaced with what was there, in
        this case zeros. The result is

        | 0000 0000 | 0000 0000 |0000 0000 | 0000 0010 |

        This is the binary representation for the number 2. The net effect is a division by two
        for each time the bit pattern is shifted. ( If the top most value was a binary one the
        value would be a negative number and the added values would have retained this
        sign by adding two more binary ones.

        Example of right shifting a negative number

        | 1111 1111  |  1111 1111 |  1111 1111  | 1111 1000 |

         int v = -8 >> 2;

        | 1111 1111  |  1111 1111 |  1111 1111 |  1111 1110  |
 

<<   The signed left shift operator works opposite to the right shift operator shifting values
        to the left, filling the least significant bit positions with zeros. The net effect of the shift is a
        multiplication of the value by two for each shift of the bits left.

>>> The unsigned right shift operator shifts in the same way the signed right shift operator
        does except it zero fills the most significant digit whether it was a 1 or a zero. Thus
        the unsigned right shift operator will always make the resultant number positive.

        //  1100 0000 0000 0000 >>> goes to 0011 0000  0000 0000  with the top bits  zero filled
 

The Comparison Operators
 

The ordinal comparison operators <   <=   >   >=

The ordinal comparison operators are used to compare numbers. They evaluate to a boolean
value, true or false.  For example,

int p = 12;
double d= 244;
if (p < d )
System.out.println(" True, p is less than d");
 

The instanceof Operator

The instanceof operator is unique in that it is represented as a keyword. It does comparison
on reference types. It returns true if the class type represented by the reference in the  left
argument is the same type represented in the right-hand argument. For this to be true the left
argument type must be the same class or a subclass of the type represented in the right-side
argument.

Example

String s ="X";
if ( s instanceof Object )
System.out.println("The boolean must evaluate to true as all classes descend from Object \n");
 

The Equality and Inequality Operators = =  and !=

= =  and  !=, are the symbols  for equality and inequality and return a boolean value. For
primitives, actual values are compared. For reference types, memory locations are compared.
These operators are not for use in comparing the contents of objects, (where the memory
address points). Doing so tests if two references point to the same object rather than if they
each contain two objects which are equivalent.

The Bitwise Operators

The bitwise operators &   |  and ^ provide bitwise AND OR and Exclusive OR
operations, taking two boolean or integral type operands.

They follow the boolean algebra rules where

1) 1 AND 1 yields 1, all other combinations produce zero.
2) 0 OR 0 yields 0, all other combinations produce 1.
3) 1 XOR 0 and 0 XOR 1 both produce 1, and any other combination results in zero.

In a boolean context the same rules apply except applied to true and false values as if they
are one and zero.

Short Circuit Operators

The short circuit operators &&||, also called the conditional AND / OR operators
only take boolean values. If the first boolean evaluates to true the second is not evaluated
There is no short circuit XOR operator. It is useful to avoid doing a test that may result in
an exception being thrown by whatever is being tested in the second boolean expression.
For example,

if ( ( theClass != null  &&  ( theClass.getTextLength( ) >1) )

This operator allows testing if a class is null and avoiding calling a method on the class if it is
which would result in a NullPointerException

The Ternary and Assignment Operators

The Ternary Operator ? ...  :

The ternary if... else operator is a short form of the if ... else construct.  It has the form

boolean bool = true ;
x = bool ? y :  z ;

where bool a boolean which, if true results in y being assigned to x, else if bool is false,
z assigned to x.  The variables x, y and z have to have to be compatible types.

Assignment Operators

The assignment operators have abbreviated combined forms such as add and assign.
An example is

i += 5;      // is the same as
i = i +5;