Вы находитесь на странице: 1из 8

J D C

T E C H

T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE

WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips,


August 15, 2000. This issue covers:
* Manipulating Java Arrays
* Java I/O Redirection
These tips were developed using Java(tm) 2 SDK, Standard Edition,
v 1.3.
You can view this issue of the Tech Tips on the Web at
http://developer.java.sun.com/developer/TechTips/2000/tt0815.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MANIPULATING JAVA ARRAYS
If you've done much Java programming, you're probably familiar
with the use of Java collections such as Vector and ArrayList.
For example, you can create a collection and add an element to
it by saying:
List lst = new ArrayList();
lst.add(new Integer(37));
In this particular example, an integer value 37 is used to
construct an Integer wrapper object, which is then added to
the list.
This simple example illustrates a fundamental point about
collections -- they are used to manipulate lists of objects,
where each object is of a class or interface type. So, for
example, an ArrayList can be composed of objects of types
such as Object, String, Float, and Runnable. Collections
do not work on lists of primitive type, such as an array
of integers.
If you're using primitive arrays in your program, how can you
manipulate them? This tip illustrates several techniques you
can use.
The first technique is sorting. The java.util.Arrays class
contains a set of class methods for sorting and searching
arrays. For example, you can say:
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/).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - JAVA I/O REDIRECTION
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 lists are used for internal Sun
Microsystems(tm) purposes only. To remove your name from a JDC
mailing list, see Subscribe/Unsubscribe below.
- FEEDBACK
Comments? Send your feedback on the JDC Tech Tips to:
jdc-webmaster@sun.com

- 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:
http://java.sun.com/jdc/
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:
http://developer.java.sun.com/developer/copyright.html
This issue of the JDC Tech Tips is written by Glen McCluskey.
JDC Tech Tips
August 15, 2000

Вам также может понравиться