2__13.rtfd -- /usr/axel/vorl/java97/skript/skript

Werbung
84
Copyright 1996-1997 by Axel T. Schreiner. All Rights Reserved.
Einen Algorithmus verkapseln
Bei den zu Java gehörigen Paketen befindet sich kein Sortierverfahren.
In diesem Abschnitt wird zuerst ein naives Sortierprogramm implementiert. Anschließend
wird eine Methodik entwickelt, mit der verschiedene Sortieralgorithmen so verkapselt
werden können, daß sie sich auf verschiedene Object-Sammlungen mit verschiedenen
Vergleichskriterien anwenden lassen. Daraus kann dann ein modulares Sortierprogramm
aufgebaut werden.
Die Quellen befinden sich in einem Katalog programs/sort , der eine Reihe von
Testprogrammen und ein Paket sort enthält, das die Sortierung verkapselt.
Es zeigt sich, daß verschiedene Entscheidungen innerhalb der zu Java gehörigen Pakete nicht
unbedingt hilfreich sind und daß das java.lang.reflect-Paket zu sehr bequemen Lösungen
führt.
Ein naives Sortierprogramm — sort/ShellSort
ShellSort sortiert seine Argumente oder die Zeilen seiner Standard-Eingabe mit dem
Sortierverfahren von Shell und gibt das Resultat als Standard-Ausgabe aus.
ShellSort ist eine sehr naive Implementierung des UNIX-Kommandos sort und demonstriert
vor allem den Umgang mit Vector und dem java.lang.reflect-Paket .
{programs/sort/ShellSort.java}
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
/** A class with naively implemented Shell sorting algorithms */
public class ShellSort {
/** collects arguments or lines, has them sorted, outputs lines */
public static void main (String args []) {
try {
if (sort(args) != null && args.length > 0)
// sort arguments
for (int n = 0; n < args.length; ++ n)
System.out.println(args[n]);
else {
// sort lines
Vector v = new Vector();
BufferedReader in =
new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = in.readLine()) != null)
v.addElement(line);
for (Enumeration e = sort(v).elements(); e.hasMoreElements(); )
System.out.println(e.nextElement());
}
} catch(Exception e) {
System.err.println(e);
}
}
{}
85
Eine Klassenmethode sort() sortiert den Argumentvektor, falls vorhanden.
Andernfalls werden Zeilen mit readLine() von BufferedReader extrahiert und als Elemente
eines Vector-Objekts gespeichert, dessen Kapazität automatisch wächst.
Eine Klassenmethode sort() sortiert den Vector für aufsteigende Indexwerte relativ zu
compareTo() aus String .
stellt mit elements() eine Enumeration seiner Elemente bereit, die dabei wirklich
vom kleinsten zum größten Index durchlaufen werden. Damit kann der sortierte Vector
ausgegeben werden.
Vector
Einen Vector sortieren
{programs/sort/ShellSort.java}
/** performs Shell sort, based on K&R */
public static Vector sort (Vector v) {
if (v != null)
for (int gap = v.size()/2; gap > 0; gap /= 2)
for (int i = gap; i < v.size(); ++ i)
for (int j = i-gap; j >= 0; j -= gap) {
String a = (String)v.elementAt(j);
String b = (String)v.elementAt(j+gap);
if (a.compareTo(b) > 0) {
v.setElementAt(b, j);
v.setElementAt(a, j+gap);
} else
break;
}
return v;
}
{}
sort() beruht auf dem Zugriff zu einzelnen Elementen im Vector und auf der Verfügbarkeit
von compareTo() — wozu aber Kenntnis der Element-Klasse nötig ist.
86
Einen Vektor sortieren
Es muß eine zweite Klassenmethode sort() geben, denn ein Vector ist kein String[].
{programs/sort/ShellSort.java}
/** performs Shell sort, based on K&R */
public static Object sort (Object v) throws NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
final int len;
if (v != null && v.getClass().isArray()
&& (len = Array.getLength(v)) > 1) {
Class elts = Array.get(v, 0).getClass();
Method cmp = elts.getMethod("compareTo", new Class[] { elts });
for (int gap = len/2; gap > 0; gap /= 2)
for (int i = gap; i < len; ++ i)
for (int j = i-gap; j >= 0; j -= gap) {
Object a = Array.get(v, j);
Object b = Array.get(v, j+gap);
if (((Integer)cmp.invoke(a, new Object[] { b })).intValue() > 0) {
Array.set(v, j, b);
Array.set(v, j+gap, a);
} else
break;
}
}
return v;
}
}
{}
Class
und das java.lang.reflect-Paket kooperieren, um zur Laufzeit Zugriff auf
Typinformation zu bieten.
Array
ermöglicht Zugriff auf
Number-Objekte
eingesetzt.
Vektoren mit beliebigen Elementen; falls nötig werden
liefert Zugriff auf beliebige Methoden per Namen, wobei ebenfalls Number-Objekte
als Argumente oder Resultate eingesetzt werden.
Method
Diese Lösung beruht auf Vektorelementen gleichen Typs und auf der Existenz einer Methode
wie sie für String — aber nicht im Bereich von Number — existiert. Da die
vorhandenen Unterklassen von Number alle final sind , wird dies trickreich...
compareTo(),
87
Verpackung der Vergleiche
Eine allgemeine Lösung sollte einen Sortieralgorithmus für Vector und Vektoren verfügbar
machen und eine Vergleichsvorschrift davon separat verkapseln.
Da Java keine Funktionszeiger besitzt, kann man ein Objekt mit dem Vergleich beauftragen:
{programs/sort/Comparer.java}
package sort;
/** An interface for objects encapsulating comparison */
public interface Comparer {
/** indicates x < == > y by returning -1 0 1 */
int compare (Object x, Object y);
}
{}
Alternativ kann man auch Vergleichbarkeit mit Hilfe einer (potentiell asymmetrischen!)
Methode verlangen:
{programs/sort/Comparable.java}
package sort;
/** An interface for comparable objects */
public interface Comparable {
/** indicates this < == > another by returning -1 0 1 */
public int compareTo (Comparable another);
}
{}
Die Algorithmus-Klasse kann das eine Interface aus dem anderen herstellen:
{programs/sort/Sorted.java}
package sort;
import java.lang.reflect.*;
import java.util.*;
/** A class to sort an Enumeration or a [] by binary insertion */
public class Sorted extends Vector {
private final static class ComparableComparer implements sort.Comparer {
public final int compare (Object x, Object y) {
return ((Comparable)x).compareTo((Comparable)y);
}
}
{}
88
Da String leider final ist, aber compareTo() besitzt
berücksichtigen:
, sollte man auch diese Situation
{programs/sort/Sorted.java}
private final static class ReflectComparer implements sort.Comparer {
private final Method compareTo;
public ReflectComparer (Object o) throws NoSuchMethodException {
Class c = o.getClass();
compareTo = c.getMethod("compareTo", new Class[] {c});
}
public final int compare (Object x, Object y) {
try {
return ((Integer)compareTo.invoke(x, new Object[] {y})).intValue();
} catch (IllegalAccessException e) {
throw new RuntimeException("compareTo: "+ e);
} catch (InvocationTargetException e) {
throw new RuntimeException("compareTo: "+ e);
}
}
}
{}
Je nach Ansatz kann man jetzt zum Beispiel auch vom ersten Objekt bestimmen, wie
verglichen werden soll, und dann ein geeignetes Vergleichsobjekt intern generieren und
speichern.
{programs/sort/Sorted.java}
/** record comparison method and sort direction<br>
* These should be final, but a blank final must be initialized in every
* initializer and jdk 1.1.5 does not analyze a call to this() correctly.
*/
private Comparer order;
private boolean up;
{}
89
Verpackung des Sortierverfahrens
Wahrscheinlich möchte man einen beliebigen Container sortieren — vermutlich, ohne seine
innere Struktur anzutasten. Ein Container sollte seine Elemente als Enumeration liefern, die
dann sortiert verfügbar sein soll.
Dazu muß man einen Index aufbauen. Diesen kann man als Vector repräsentieren und schon
beim Einfügen sortieren. Aus Effizienzgründen verwendet man binäre Suche, aber dadurch
wird nicht stabil sortiert:
{programs/sort/Sorted.java}
/** binary insertion of Enumeration into this, sorted up (or down) */
public final void insertSorted (Enumeration e) {
if (e != null)
while (e.hasMoreElements()) {
Object o = e.nextElement();
// to insert
int low = 0;
for (int n = elementCount, high = n-1; low <= high; n >>= 1) {
int mid = low + (n >> 1);
int c = order.compare(o, elementData[mid]);
if (c == 0) { low = mid; break; }
// found at mid
if (c < 0 == up) high = mid-1;
// insert before mid
else { low = mid+1; -- n; }
// insert after mid
}
insertElementAt(o, low);
}
}
{}
ist true oder false und legt einfach die Interpretation des Vergleichs fest, wenn die
beteiligten Objekte nicht gleich sind.
up
Andere Verfahren, insbesondere Nachbessern eines vorhandenen Index, kann man ähnlich
verpacken.
90
Aufbereitung als Enumeration — Elements und Lines
{programs/sort/Elements.java}
package sort;
import java.lang.reflect.Array;
import java.util.*;
/** A class to enumerate the elements in a [] or a single Object */
public class Elements implements Enumeration {
private Object v;
// to get elements from
private int next = 0, len = 0;
// lookahead
public Elements (Object v) {
// remember v
if ((this.v = v) != null)
len = v.getClass().isArray() ? Array.getLength(v) : 1;
}
public boolean hasMoreElements () {
return next < len;
}
public Object nextElement () {
if (!hasMoreElements()) throw new NoSuchElementException();
++ next;
return v.getClass().isArray() ? Array.get(v, next-1) : v;
}
}
{programs/sort/Lines.java}
package sort;
import java.io.*;
import java.util.*;
/** A class to enumerate the lines in an InputStream */
public class Lines extends BufferedReader implements Enumeration {
private String next;
// not null? one line lookahead
private boolean eof;
// set at eof
public Lines (Reader in, int sz)
{ super(in, sz); }
public Lines (Reader in)
{ super(in); }
public Lines (InputStream in)
{ this(new InputStreamReader(in)); }
public boolean hasMoreElements () {
if (!eof && next == null)
try {
next = readLine(); eof = next == null;
} catch (IOException e) {
next = null; eof = true;
}
return next != null;
// if true: next is set
}
public Object nextElement () {
if (!hasMoreElements()) throw new NoSuchElementException();
Object result = next; next = null;
return result;
}
}
{}
Mit Elements kann man ein Objekt oder einen Vektor als Enumeration liefern, mit Lines einen
InputStream oder Reader.
91
Konstruktoren
Man sollte einen Comparer, eine Enumeration und die Sortierrichtung in verschiedenen
Kombinationen angeben können. Wenn möglich, sollte man auch die Kapazität vorplanen:
{programs/sort/Sorted.java}
public Sorted (Enumeration e) throws NoSuchMethodException {
this(e, 0, true);
}
public Sorted (Enumeration e, boolean up) throws NoSuchMethodException {
this(e, 0, up);
}
public Sorted (Enumeration e, int initialCapacity)
throws NoSuchMethodException {
this(e, initialCapacity, true);
}
public Sorted (Enumeration e, int initialCapacity, boolean up)
throws NoSuchMethodException {
this.up = up;
if (initialCapacity > 0) ensureCapacity(initialCapacity);
addElement(e.nextElement());
// must exist to set order
if (elementData[0] instanceof Comparable)
order = new ComparableComparer();
else
order = new ReflectComparer(elementData[0]);
insertSorted(e);
}
public Sorted (Comparer order) {
this(order, true);
}
public Sorted (Comparer order, boolean up) {
this.order = order; this.up = up;
}
public Sorted (Enumeration e, Comparer order) {
this(e, 0, order, true);
}
public Sorted (Enumeration e, Comparer order, boolean up) {
this(e, 0, order, up);
}
public Sorted (Enumeration e, int initialCapacity, Comparer order) {
this(e, initialCapacity, order, true);
}
public Sorted (Enumeration e, int initialCap, Comparer order, boolean up) {
this(order, up);
if (initialCap > 0) ensureCapacity(initialCap);
insertSorted(e);
}
}
{}
Mit Hilfe von Elements kann man Sorted auch aus einzelnen Objekten oder Vektoren
konstruieren. Aus Effizienzgründen sollte man vor insertSorted() zuerst ensureCapacity()
aufrufen.
92
Tests
Mit einer Reihe von trivialen Testprogrammen untersucht man, wie bequem Sorted für
verschiedene Datenmengen zu verwenden ist.
Print — ausgeben
gibt verschiedene Datenmengen zeilenweise aus. Print demonstriert dies durch
Ausgabe der Kommandozeile:
Print
{programs/sort/Print.java}
package sort;
import java.io.*;
import java.util.*;
/** A local class to print a Vector or print and consume an
public class Print {
public static void print (PrintStream p, Enumeration e) {
while (e.hasMoreElements())
p.println(e.nextElement());
}
public static void out (Enumeration e){ print(System.out,
public static void out (Vector v)
{ print(System.out,
public static void out (Object o)
{ print(System.out,
public static void err (Enumeration e){ print(System.err,
public static void err (Vector v)
{ print(System.err,
public static void err (Object o)
{ print(System.err,
/** prints arguments from the command line */
public static void main (String args []) {
Print.out(args);
}
}
enumeration */
e); }
v.elements()); }
new Elements(o)); }
e); }
v.elements()); }
new Elements(o)); }
{}
A — Kommandozeile sortiert ausgeben
A gibt die Kommandoargumente sortiert zeilenweise aus. A demonstriert Sorted für einen
Vektor von String-Objekten, die mit einem ReflectComparer verglichen werden:
import sort.Print;
import sort.Elements;
import sort.Sorted;
{programs/sort/A.java}
// A lives in same directory as sort package
/** A trivial class to test Sorted for String[] */
public class A {
public static void main (String args []) {
try {
Print.out(new Sorted(new Elements(args), args.length));
} catch (Exception e) {
e.printStackTrace(); System.exit(1);
}
}
}
{}
93
B — Eingabezeilen sortiert ausgeben
B gibt Eingabezeilen sortiert aus. B demonstriert Sorted für eine Enumeration von
String-Objekten, die mit einem ReflectComparer verglichen werden:
{programs/sort/B.java}
import sort.Print;
import sort.Sorted;
import sort.Lines;
/** A class to sort input lines as an Enumeration of String, up [or down] */
public class B {
public static void main (String args []) {
try {
Print.out(new Sorted(new Lines(System.in), args.length == 0));
} catch (Exception e) {
e.printStackTrace(); System.exit(1);
}
}
}
{}
C — System-Properties sortiert ausgeben
C gibt die System-Properties sortiert zeilenweise aus. C demonstriert, wie man Schlüssel
sortieren und dann mit Werten ausgeben kann:
{programs/sort/C.java}
import sort.Print;
import sort.Sorted;
import java.util.*;
/** A trivial class to emit system properties sorted up [or down] */
public class C {
public static void main (String args []) {
try {
Enumeration n = System.getProperties().propertyNames();
n = new Sorted(n, args.length == 0).elements();
while (n.hasMoreElements()) {
String s = n.nextElement().toString();
System.out.println(s+"\t"+System.getProperty(s));
}
} catch (Exception e) {
e.printStackTrace(); System.exit(1);
}
}
}
{}
94
D— System-Properties sortiert ausgeben
D gibt die System-Properties sortiert zeilenweise aus.
D faßt jeweils einen Namen und einen Wert zusammen und leitet sie als neue Enumeration
weiter. D demonstriert Sorted für eine Enumeration von Comparable-Objekten:
{programs/sort/D.java}
import
import
import
import
sort.Print;
sort.Sorted;
sort.Comparable;
java.util.*;
/** A class to emit system properties using associations sorted up [or down] */
public class D implements Enumeration {
private final static class KV implements Comparable {
private final String key, value;
public KV (String key, String value) {
this.key = key; this.value = value;
}
public int compareTo (Comparable another) {
if (another instanceof KV)
return key.compareTo(((KV)another).key);
throw new IllegalArgumentException("compareTo: expect key/value");
}
public String toString () {
return key +"\t"+ value;
}
}
private Enumeration names = System.getProperties().propertyNames();
public boolean hasMoreElements () {
return names.hasMoreElements();
}
public Object nextElement () {
String name = (String)names.nextElement();
return new KV(name, System.getProperty(name));
}
public static void main (String args []) {
try {
Print.out(new Sorted(new D(), args.length == 0));
} catch (Exception e) {
e.printStackTrace(); System.exit(1);
}
}
}
{}
95
E— Gleitkomma-Zahlen sortiert ausgeben
E liest Gleitkommawerte von Eingabezeilen und gibt sie sortiert zeilenweise aus.
E leitet die Werte als neue Enumeration von Float-Objekten weiter und verwendet einen
Comparer:
{programs/sort/E.java}
import
import
import
import
import
sort.Print;
sort.Sorted;
sort.Lines;
sort.AsFloat;
java.util.*;
/** A class to take Floats from input lines and sort them up [or down] */
public class E implements Enumeration {
private Enumeration values = new Lines(System.in);
public boolean hasMoreElements () {
return values.hasMoreElements();
}
public Object nextElement () {
String s = (String)values.nextElement();
for (;;)
try {
return new Float(s);
} catch (NumberFormatException e) {
if (s.length() <= 1) return new Float(0);
s = s.substring(0, s.length()-1);
}
}
public static void main (String args []) {
try {
Print.out(new Sorted(new E(), new AsFloat(), args.length == 0));
} catch (Exception e) {
e.printStackTrace(); System.exit(1);
}
}
}
{programs/sort/AsFloat.java}
package sort;
Das
/** A class to compare Number objects as float values */
public class AsFloat implements Comparer {
public int compare (Object a, Object b) {
if (a == b || (a != null && a.equals(b))) return 0;
if (a instanceof Number && b instanceof Number) {
float af = ((Number)a).floatValue(), bf = ((Number)b).floatValue();
if (af == bf) return 0;
return af < bf ? -1 : 1;
}
throw new IllegalArgumentException("compareTo: expecting Number");
}
}
{}
sort-Paket sollte möglichst viele derartige Comparer bereitstellen.
Herunterladen