Java Arrays

From JavaProgramming

Arrays and strings in Java

Warning: There be dragons here... [I just refactored them away, hopefully I got them all]

Thanks, actually I meant that the topic itself is fraught with traps, pitfalls, and landmines. See JavaArraysBreakTypeSafety and JavaArraysShouldBeFirstClassObjects, for example.


Arrays have constant size:

 int arr[] = new int[3];

If more space is needed at run time, a new array object must be created, and you must manually copy any data you still need from the old array.

Use collection classes, such as java.util.ArrayList for dynamic arrays.

The similar legacy class java.util.Vector has some overhead because it is completely thread-safe. If you do not require thread-safety at that level, the modern equivalent java.util.ArrayList does not pay the synchronization cost, but can still be wrapped with a synchronization wrapper using java.util.Collections.synchronizedList(). The cost of acquiring a lock on a monitor in the current virtual machines is much lower than it once was, so although the penalty is low now, it was once quite severe.

Arrays may be multidimensional:

 int arr2[][] = new int[3][2];

Array declarations may have the [] before or after the variable name:

 int[] foo;
 int   bar[];

Arrays may be initialized in code:

 int ages[] = { 3, 6, 7 };

Multidimensional arrays may be non-rectangular:

 int ragged[][] = { {1, 2}, {3, 4, 5}, {6} };


The String class has no index-operator, use method charAt():

 //char c = s[0]; // Index operator not supported
 char c = s.charAt(0); // Ok

The + operator performs concatenation in expressions that have a String operand. Beyond this compiler-based "overloading", operator overloading is not supported in Java.

Thus, strings may be concatenated with other strings or expressions of other types:

 String s = "number: " + 10;
String concatenation with other object types implicitly invokes toString() on the operand.

 Date d = new Date();
 String s = "now: " + d;
Prints something like: "now: Wed Jun 26 14:25:08 MDT 2002"

For Strings -- and indeed all object types -- the equality operator (==) tests for object identity, not lexical equivalence. Use equals() to test for lexical equivalence between strings, and use == only to test if two references point to the exact same string instance:

 String s1 = "123", s2 = s1, s3 = new String(s1);
 System.out.println(s1 == s2);  // prints true
 System.out.println(s1 == s3);  // prints false
 System.out.println(s1.equals(s2));  // prints true
 System.out.println(s1.equals(s3));  // prints true

It is possible to use the intern() function to use == for comparison: s1.equals(s2) if and only if s1.intern() == s2.intern(). Of course the gain is to store the result of s.intern(), and then get faster comparisons.

Interesting sidebar on the original text: this example would not demonstrate what the original author intended.

 String s1 = "123", s2 = "123";
 // boolean b = (s1 == s2);  // Equality operator not supported  [[wrong]]
 boolean b = s1.equals(s2); // OK! 

Sun's compiler actually distills all identical string literals in a class down to a single string object in the constant pool of that class. The == test shown here would actually evaluate as true! The example was rewritten, in a somewhat less lucid form, for this reason.

Conventional wisdom has advised to use StringBuffer profusely, however such advice is mostly outdated since current Java compilers will use StringBuffer in the generated code. The original exhange retained below tells most of the story, and I will return with more examples. The current compilers do a pretty good job on complex string expressions.

 final long yarms = 3;
 String s = "There are " + 4 + " arms on the capital letter " + 'X' + " and "
   + " there are only " + yarms + " on the letter " + 'Y' + ".";

 String s = "There are 4 arms on the capital letter X and  there are only 3 on the letter Y.";
Don't trust me on this... use javap -c and see for yourself! (javap comes with your JDK.)

For me, the moral of the String/StringBuffer story is to be careful with performance advice from Java book authors. Most Java performance tricks fall out of date as the compilers approach professional quality and as the runtime environments improve. (In fact, some of the tricks that improved performance under earlier Java releases actually hinder performance under later versions of Java!)

[original exchange on String and StringBuffer]]
"Use class StringBuffer for better performance. String concatenation will result in a new string object, whereas the content of a StringBuffer object is mutable:

 String s = "abc";
 s += "def";	// Creating new String object
 Stringbuffer sb = "abc";
 sb.append("def"); // StringBuffer object now contains "abcdef" 

Note, that String concatenation uses a StringBuffer internally, the above is executed as such:
 String s = "abc";
 StringBuffer sb = new StringBuffer(s);
and therefore there is no performance benefit in most cases, unless you're concatting many, many strings; StringBuffer is not a String, and therefore cannot be used in place of a String, but starting with JDK 1.4 String and StringBuffer both implement the (small) interface CharSequence.

Here is a test program for trying out array and string functions: [Dragons remain]

 /* */
 public class TestString {
	public static void main(String[] args) {
	int arr[] = new int[3];
	arr[0] = 1;
	arr = new int[4];		// Data lost on creation of a new array
	System.out.println("" + arr[0]); // Output = 0

String s = "123"; // if(s[0] == '1') // Index operator not supported if(s.charAt(0) == '1') // Ok, output: true System.out.println("true"); else System.out.println("false"); // or to a Java programmer: System.out.println(s.charAt(0) == '1'); :-)

// Arithmetic operators supported for String s = "number: " + 10; // Implicit call to String() and Integer.toString() [actually, it gets folded together at compile time] System.out.println(s); // output: number: 10

s = Integer.toString(10); // Explicit conversion System.out.println(s); // output: 10

String s1 = "123", s2 = "123"; // [Grrrrowwl] // if(s1 == s2) System.out.println("true"); // Equality operator not supported // ["not supported" overstates things... "doesn't do what some might expect".]

if(s1.equals(s2)) // Output: true System.out.println("true"); else System.out.println("false"); // Use StringBuffer for better performance [ummm...] s = "abc"; s += "def"; // Creating new String object System.out.println(s); // output: abcdef StringBuffer sb = new StringBuffer("abc"); sb.append("def"); // StringBuffer object now contains "abcdef" System.out.println(sb); // output: abcdef } }

StringBuffer improves upon String when a loop comes into play.

  String[] term = new String[1000];
  for (int i = 0; i < term.length; ++i) {
    term[i] = "term " + i + ",";

// Now start timing.

String stringConcatenation = ""; for (int i = 0; i < term.length; ++i) { stringConcatenation += term[i]; }

// Compare to:

StringBuffer stringBufferConcatenation = new StringBuffer(); for (int i = 0; i < term.length; ++i) { stringBufferConcatenation.append(term[i]); } String sbFinal = stringBufferConcatenation.toString();

It would be a very sophisticated compiler that converted the String loop to the StringBuffer form. And, if the variables were attributes instead of method-local variables, it wouldn't even be correct, considering the possibilities of multi-threading. --EricJablow

View edit of December 17, 2004 or FindPage with title or text search