|
August 15, 2000 WELCOME to the Java Developer Connection (JDC) Tech Tips, August 15, 2000. This issue covers: This issue of the JDC Tech Tips is written by Glen McCluskey. These tips were developed using Java 2 SDK, Standard Edition, v 1.3.
|
import java.util.Arrays;
public class ArrayDemo1 {
public static void main(String args[]) {
int vec[] = {37, 47, 23, -5, 19, 56};
Arrays.sort(vec);
for (int i = 0; i < vec.length; i++) {
system.out.println(vec[i]);
}
}
}
|
The demo initializes an array of integers, and then calls
Arrays.sort to sort the array in ascending order.
In a similar way, you can do binary searching on a sorted array:
import java.util.Arrays;
public class ArrayDemo2 {
public static void main(String args[]) {
int vec[] = {-5, 19, 23, 37, 47, 56};
int slot = Arrays.binarySearch(vec, 35);
slot = -(slot + 1);
System.out.println("insertion point = " + slot);
}
}
|
There's one tricky aspect of this particular program. If the binary search fails to find the requested element in the array, it returns:
-(insertion point) - 1
The demo program calls the search method with an argument of 35, which is not in the array. The method returns a value of -4. If the value -4 is incremented and then negated, the result is 3; this is the point at which 35 would be inserted into the array. In other words, the values -5, 19, and 23 occupy locations 0, 1, and 2 in the array. The value 35 therefore belongs at index 3, with the values 37, 47, and 56 pushed down. The search method does not actually insert 35 into the array, but just indicates where it should go.
Beyond sorting and searching, what else can you do with primitive arrays? Another useful technique is to take an array of primitive type and turn it into an equivalent Object array. Here each corresponding element is a wrapped version of the primitive element. So, for example, a value 37 in the primitive array becomes Integer(37) in the wrapped array.
Here's how you can do this:
import java.util.Arrays;
import java.lang.reflect.Array;
public class ArrayDemo3 {
// if input is a single-dimension primitive array,
// return a new array consisting of wrapped elements,
// else just return input argument
public static Object toArray(Object vec) {
// if null, return
if (vec == null) {
return vec;
}
// if not an array or elements not primitive, return
Class cls = vec.getClass();
if (!cls.isArray()) {
return vec;
}
if (!cls.getComponentType().isPrimitive()) {
return vec;
}
// get array length and create Object output array
int length = Array.getLength(vec);
Object newvec[] = new Object[length];
// wrap and copy elements
for (int i = 0; i < length; i++) {
newvec[i] = array.get(vec, i);
}
return newvec;
}
public static void main(string args[]) {
// create a primitive array
int vec[] = new int[]{1, 2, 3};
// wrap it
object wrappedvec[] = (object[])toarray(vec);
// display result
for (int i = 0; i < wrappedvec.length; i++) {
system.out.println(wrappedvec[i]);
}
}
}
|
The method "toArray" takes a single Object argument (arrays can be
assigned to Object references). If the argument is null, does not
represent an array, or represents an array of other than primitive
type, the method simply returns the argument. java.lang.Class
facilities are used to determine whether the argument is an array,
and to obtain the array's underlying component type.
Once these checks are made, reflection facilities from the class
java.lang.reflect.Array are used to obtain the length of the
primitive array, and then obtain individual elements of the array.
Each element obtained by Array.get is returned in a wrapper such
as Integer or Double.
A final example builds on the previous one, and shows how you can use collection features with arrays. This assumes you already have an Object array. Here's the program:
import java.util.Arrays;
import java.util.List;
public class ArrayDemo4 {
public static void main(String args[]) {
Object vec[] = {new Integer(37), new Integer(47)};
List lst = Arrays.asList(vec);
lst.set(1, new Integer(57));
for (int i = 0; i < vec.length; i++) {
system.out.println(vec[i]);
}
}
}
|
In this program, vec is an Object array containing Integer(37)
and Integer(47) objects. Then Arrays.asList is called. It returns
a collection (of interface type List), with the array as the
backing storage for the collection. In other words, a collection
like ArrayList has within it some type of storage to store
collection elements. In this example, the storage that is used is
the passed-in array argument to Arrays.asList. This means that
changes made by collection methods (such as set), are reflected
in the underlying array.
Changing the element at index 1 in the collection causes the underlying array to change, and the output of running the program is:
37
57
So if you have an Object array, you can use collection features with it; the array itself serves as the underlying storage.
It's also possible to take a collection and convert it to an Object array, by saying:
Object vec[] = lst.toArray();
For further information, see sections 11.2.9 Arrays, 16.6 List,
and 16.9 The Arrays Utility Class in "The Java Programming
Language Third Edition" by Arnold, Gosling, and Holmes
(http://java.sun.com/docs/books/javaprog/thirdedition/)
If you've worked with UNIX or Windows shells (command processors) very much, you've probably used I/O redirection operators such as this:
$ command >outfile
This usage says to run a command, and direct its standard output
(such as that written by System.out.println) to the specified file
instead of to the console or screen.
This feature is quite useful. However it's possible to achieve the same objective inside of a Java application, without relying on a shell. Typically, if you're using a programming style that relies on standard input and output (as the UNIX shell and utility programs do), you don't need or want to redirect I/O from inside a program. But sometimes there are exceptions to this rule. Let's look at a couple of examples.
The first example redirects standard output to a file:
import java.io.*;
public class RedirectDemo1 {
public static void main(String args[]) throws IOException {
// set up a PrintStream pointing at a file
FileOutputStream fos =
new FileOutputStream("out.txt");
BufferedOutputStream bos =
new BufferedOutputStream(fos, 1024);
PrintStream ps =
new PrintStream(bos, false);
// redirect System.out to it
System.setOut(ps);
// do some output
System.out.println("This is a test\u4321");
int n = 37;
System.out.println("The square of " + n +
" is " + (n * n));
ps.close();
}
}
|
Standard output (System.out) is a reference to an object of type
PrintStream. To redirect output, an object of this type is
created, representing a file or other output stream (such as
a network connection). Then System.setOut is called to change
the System.out reference.
As a point of interest, the JDK 1.3 implementation of
java.lang.System has internal code of this form:
FileOutputStream fdOut =
new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(
new BufferedOutputStream(fdOut, 128), true));
This code is used to initialize System.out. So, by default,
System.out is a PrintStream. The PrintStream represents a special
FileOutputStream file created from FileDescriptor.out, with
a 128-byte buffer, and with autoflush set to true. Autoflush means
that output is flushed on new line characters and when byte
vectors are written. When output is redirected as in the example
above, System.out is changed to reference the new PrintStream
object that was created and passed to System.setOut.
There are a couple of issues related to the RedirectDemo1 program. One is that PrintStream converts characters to bytes using the platform's default character encoding. This is usually a good thing. For example, if you have a short Java program with the following line in it:
System.out.println("this is a test");
and you run the program by saying (in the United States English locale):
$ java prog >outfile
the program will direct ASCII characters to "outfile". This is probably what you want, given the prevalence of 7-bit ASCII text files.
But there's a problem in that the Unicode character '\u4321'
in the demo program has turned into a single '?' byte. You can
see the ? if you look at the out.txt file. In other words, the
default encoding doesn't correctly handle one of the output
characters.
Another issue is that I/O redirection can be generalized. You can, for example, send output to a string instead of to a file. Here's an example that covers both of these issues:
import java.io.*;
public class RedirectDemo2 {
public static void main(String args[]) throws IOException {
// set up a PrintWriter layered
// on top of a StringWriter
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
// write some output (to the StringWriter)
pw.println("This is a test\u4321");
int n = 37;
pw.println("The square of " + n + " is " + (n * n));
// get a string containing what was written
String str = sw.toString();
// display it
System.out.print(str);
// output the string to a file, using
// the UTF-8 encoding
FileOutputStream fos =
new FileOutputStream("out.txt");
OutputStreamWriter osw =
new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw =
new BufferedWriter(osw);
bw.write(str);
bw.close();
// read back the string and check
FileInputStream fis =
new FileInputStream("out.txt");
InputStreamReader isr =
new InputStreamReader(fis, "UTF-8");
BufferedReader br =
new BufferedReader(isr);
String s1 = br.readLine();
String s2 = br.readLine();
br.close();
String linesep = System.getProperty("line.separator");
if (!str.equals(s1 + linesep + s2 + linesep))
System.err.println("equals check failed");
}
}
|
The first part of the example sets up a PrintWriter object layered on top of a StringWriter. PrintWriter is similar to PrintStream, but operates on characters instead of byte streams. StringWriter is used to accumulate characters into a dynamic internal buffer for later retrieval into a String or StringBuffer.
After the output is written to the StringWriter, the accumulated string is retrieved. The string is then written to a file using OutputStreamWriter and the UTF-8 encoding. This encoding is supported in all Java implementations. It encodes Java characters in the range '\u0001' through '\u007f' as one byte; other characters are encoded as two or three bytes.
Finally, the string is read back from the file, again using the UTF-8 encoding. The string is then compared to the original string. The original string had two line separators in it, and so it is read back as two strings. The line separators are added to build up one string for comparison.
Note that you can also redirect input from a file or string, using classes such as StringReader.
Further reading: sections 9.7.1 Character Encoding, 15.4.7
String Character Streams, 15.4.8 Print Streams, and 18.1.1
Standard I/O Streams in "The Java Programming Language Third
Edition" by Arnold, Gosling, and Holmes
(http://java.sun.com/docs/books/javaprog/thirdedition/)
Note
The names on the JDC mailing list are used for internal Sun MicrosystemsTM purposes only. To remove your name from the list, see Subscribe/Unsubscribe below.
Feedback
Comments? Send your feedback on the JDC Tech Tips to: jdc-webmaster
Subscribe/Unsubscribe
The JDC Tech Tips are sent to you because you elected to subscribe. To unsubscribe from this and any other JDC Email, select "Subscribe to free JDC newsletters" on the JDC front page. This displays the Subscriptions page, where you can change the current selections.
You need to be a JDC member to subscribe to the Tech Tips. To become a JDC member, go to:
To subscribe to the Tech Tips and other JDC Email, select "Subscribe to free JDC newsletters" on the JDC front page.
Archives
You'll find the JDC Tech Tips archives at:
http://developer.java.sun.com/developer/TechTips/index.html
Copyright
Copyright 2000 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.
This Document is protected by copyright. For more information, see:
|
| ||||||||||||