Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
gp Kapitel 11 Datenstrukturen und Algorithmen
  gp 11.1 Mit einem Iterator durch die Daten wandern
    gp 11.1.1 Die Schnittstellen Enumeration und Iterator
  gp 11.2 Datenstrukturen und die Collection-API
    gp 11.2.1 Die Schnittstelle Collection
    gp 11.2.2 Das erste Programm mit Container-Klassen
    gp 11.2.3 Generische Datentypen in der Collection-API
    gp 11.2.4 Der Iterator, die Schnittstelle Iterable und das erweiterte for
    gp 11.2.5 Schnittstellen, die Collection erweitern, und Map
    gp 11.2.6 Konkrete Container-Klassen
  gp 11.3 Listen
    gp 11.3.1 AbstractList
    gp 11.3.2 Beispiel mit List-Methoden
    gp 11.3.3 ArrayList
    gp 11.3.4 asList() und die »echten« Listen
    gp 11.3.5 toArray() von Collection verstehen – die Gefahr einer Falle erkennen
    gp 11.3.6 Die interne Arbeitsweise von ArrayList und Vector
    gp 11.3.7 LinkedList
  gp 11.4 Stack (Kellerspeicher, Stapel)
    gp 11.4.1 Die Methoden von Stack
    gp 11.4.2 Ein Stack ist ein Vector – aha!
  gp 11.5 Queues (Schlangen)
    gp 11.5.1 Blockierende Queues und Prioritätswarteschlangen
  gp 11.6 Assoziative Speicher und die Klasse HashMap
    gp 11.6.1 Ein Objekt der Klasse HashMap erzeugen
    gp 11.6.2 Einfügen und Abfragen der Datenstruktur
    gp 11.6.3 Wichtige Eigenschaften von Assoziativspeichern
    gp 11.6.4 Elemente im Assoziativspeicher müssen unveränderbar bleiben
    gp 11.6.5 Die Arbeitsweise einer Hash-Tabelle
    gp 11.6.6 Aufzählen der Elemente
    gp 11.6.7 Der Gleichheitstest und der Hash-Wert einer Hash-Tabelle
    gp 11.6.8 Klonen
  gp 11.7 Die Properties-Klasse
    gp 11.7.1 Properties setzen und lesen
    gp 11.7.2 Properties verketten
    gp 11.7.3 Eigenschaften ausgeben
    gp 11.7.4 Hierarchische Eigenschaften
    gp 11.7.5 Properties speichern
    gp 11.7.6 Über die Beziehung Properties und Hashtable
  gp 11.8 Mengen (Sets)
    gp 11.8.1 HashSet
    gp 11.8.2 TreeSet – die Menge durch Bäume
  gp 11.9 Algorithmen in Collections
    gp 11.9.1 Datenmanipulation: Umdrehen, Füllen, Kopieren
    gp 11.9.2 Vergleichen von Objekten mit Comparator und Comparable
    gp 11.9.3 Größten und kleinsten Wert einer Collection finden
    gp 11.9.4 Sortieren
    gp 11.9.5 nCopies()
    gp 11.9.6 Singletons
    gp 11.9.7 Elemente in der Collection suchen
  gp 11.10 Synchronisation der Datenstrukturen
  gp 11.11 Die abstrakten Basisklassen für Container
    gp 11.11.1 Optionale Methoden
  gp 11.12 Die Klasse BitSet für Bitmengen
    gp 11.12.1 Ein BitSet anlegen und füllen
    gp 11.12.2 Mengenorientierte Operationen
    gp 11.12.3 Funktionsübersicht
    gp 11.12.4 Primzahlen in einem BitSet verwalten
  gp 11.13 Ein Design-Pattern durch Beobachten von Änderungen
    gp 11.13.1 Design-Pattern
    gp 11.13.2 Das Beobachter-Pattern (Observer/Observable)


Galileo Computing

11.7 Die Properties-Klasse  downtop

Die Klasse Properties ist eine Sonderform der Assoziativspeicher, die Schlüssel/Wertepaare aus Strings verwaltet. Sie können später in einer Datei gespeichert und wieder ausgelesen werden. Auf diese Weise lassen sich zum Beispiel Zeichenketten aus dem Programmtext herausziehen und externalisieren, so dass auch ohne Neuübersetzung die Werte bequem verändert werden können.


Galileo Computing

11.7.1 Properties setzen und lesen  downtop

Um ein Properties-Objekt zu füllen, wird die Funktion setProperty() eingesetzt. Die Argumente sind zwei Zeichenketten, der Schlüssel und der Wert. Um später wieder an den Schlüssel zu kommen, wird getProperty() mit dem Schlüssel aufgerufen, und liefert dann – wenn beide Zeichenketten vorher verbunden wurden – den Wert.


Properties props = new Properties(),

props.setProperty( "User", "King Karl" );
props.setProperty( "Version", "" + 0.02 );
System.out.println( props.getProperty("User") );      // King Karl
System.out.println( props.getProperty("Passwort") );  // null

Galileo Computing

11.7.2 Properties verketten  downtop

Wenn ein Properties-Objekt einen Schlüssel nicht findet, dann kann es sich an ein anderes Properties-Objekt wenden, so dass eine Liste von Quellen entsteht. Das Eltern-Properties-Objekt, wird einfach im Konstruktor übergeben.


Beispiel   Das nachfolgende Programm erzeugt zwei Properties-Objekte. Eine davon ist eine Standardliste und die andere soll eine benutzerdefinierte sein, die anfänglich alle Einstellungen von der Standardliste übernimmt.

Listing 11.5   TestProperties.java


import java.util.Properties;

class TestProperties
{
  public static void main( String args[] )
  {
    Properties defaultProperties = new Properties(),
               userProperties = new Properties(defaultProperties);

    defaultProperties.setProperty( "User", "Standard" );
    defaultProperties.setProperty( "Version", "" + 0.02f );
    userProperties.setProperty( "User", "Ulli Schnulli" );
    userProperties.setProperty( "MagCleverUndSmart", "" + true );

    System.out.println( "Default Properties:" );
    defaultProperties.list( System.out );

    System.out.println( "\nUser Properties:" );
    userProperties.list( System.out );

    System.out.println( "Property ’User’ is ’" +
                        userProperties.getProperty("User") + "’" );
    }
}

Testen wir das Programm, so fällt die Weiterleitung auf: Obwohl wir zunächst das Default- und User-Objekt erzeugen, werden die später zum Default-Objekt hinzugefügten Daten an das User-Objekt weitergereicht. Nachträglich dem Default-Objekt zugeordnete Werte kommen also auch zum Aufrufer zurück. Die Implementierung zeigt: Zuerst durchsucht ein Property-Exemplar die eigene, in ihm enthaltene Hash-Tabelle. Liefert diese keinen Eintrag oder keinen Wert vom Typ String, so wird das im Konstruktoraufruf angegebene Property-Objekt durchsucht. Auf diese Weise lassen sich mehrstufige Hierarchien von Property-Verzeichnissen konstruieren. Über list() ergibt sich die Ausgabe:


Default Properties:
-- listing properties --
Version=0.02
User=Standard

User Properties:
-- listing properties --
MagCleverUndSmart=true
Version=0.02
User=Ulli Schnulli
PropertyUserisUlli Schnulli


class java.util.  Properties  
extends Hashtable<Object,Object>

gp  Properties()
Erzeugt ein leeres Properties-Objekt ohne Schlüssel und Werte.
gp  Properties( Properties defaults )
Erzeugt ein leeres Properties-Objekt, das bei Anfragen auch auf die Einträge in dem übergebenen Properties-Objekt zurückgreift.
gp  String getProperty( String key )
Sucht in den Properties nach der Zeichenkette key als Schlüssel und liefert den zugehörigen Wert. Durchsucht auch nachgeschaltete Properties-Objekte.
gp  String getProperty( String key, String default )
Sucht in den Properties nach der Zeichenkette key als Schlüssel und liefert den zugehörigen Wert. Ist der Schlüssel nicht vorhanden, wird der String default zurückgegeben.
gp  Object setProperty( String key, String value )
Trägt Schlüssel und Wert im Properties-Exemplar ein. Existiert der Schlüssel schon, wird er überschrieben. Mitunter verdeckt der Schlüssel den Wert der Property in der übergeordneten Property.

Galileo Computing

11.7.3 Eigenschaften ausgeben  downtop

Die list()-Methode wandert durch die Daten eines Properties-Exemplars und schreibt sie in einen PrintStream oder PrintWriter. Das sind Datenströme, denen wir uns näher im Eingabe- und Ausgabekapitel widmen wollen. Eine Ausgabe auf dem Bildschirm erhalten wir mit list(System.out). Schlüssel und Werte werden von einem Gleichheitszeichen getrennt. Die Ausgabe über list() ist gekürzt, denn ist ein Wert länger als 40 Zeichen, so wird er abgekürzt. Den Paaren geht eine Kopfzeile der Art -- listing properties -- voran. Es ist wichtig zu verstehen, dass durch die Art der Speicherung (ein Assoziativspeicher auf Basis des Hashings) die Ausgabe unsortiert erfolgt.



class java.util.  Properties  
extends Hashtable<Object,Object>

gp  void list( PrintStream out )
Listet die Properties auf dem PrintStream aus.
gp  void list( PrintWriter out )
Listet die Properties auf dem PrintWriter aus.

Galileo Computing

11.7.4 Hierarchische Eigenschaften  downtop

Leider kann eine Eigenschaften-Datei nicht segmentiert werden, wie etwa alte Windows INI-Dateien dies machen. Die Alternative ist, hierarchisch benannte Eigenschaften erzeugen, in dem eine Zeichenkette vor jedem Schlüssel gesetzt wird. Um zum Beispiel einen Schlüssel User einmal unter Private und einmal unter Public zu halten, lässt sich die Eigenschaft Private.User und Public.User einsetzen. Doch leider tauchen sie dann gespeichert quer gewürfelt in der Datei auf, da ein Assoziativspeicher keine Sortierung besitzt.


Galileo Computing

11.7.5 Properties speichern  downtop

Während die list()-Funktion nur für Testausgaben gedacht ist, dient store() zum Speichern und load() zum Laden eines Properties-Objektes in einer ASCII-Datei, die Schlüssel und Werte mit einem Gleichheitszeichen trennt.

Das folgende Beispiel initialisiert ein Properties-Objekt mit den Systemeigenschaften und fügt dann einen Wert hinzu. Anschließend werden die Daten persistent gemacht, wieder gelesen und mit der list()-Methode auf dem Bildschirm ausgeben.

Listing 11.6   SaveProperties.java


import java.io.*;
import java.util.*;

class SaveProperties
{
  public static void main( String args[] )
  {
   String filename = "properties.txt";
    try
    {
      FileOutputStream propOutFile =
         new FileOutputStream( filename );

      Properties p1 = new Properties( System.getProperties() );

      p1.setProperty( "MeinNameIst", "Forest Gump" );
      p1.store( propOutFile, "Eine Insel mit zwei Bergen" );

      FileInputStream propInFile = new FileInputStream( filename );

      Properties p2 = new Properties();
      p2.load( propInFile );

      p2.list( System.out );
     }
     catch ( FileNotFoundException e ) {
       System.err.println( "Can’t find " + filename );
     }
     catch ( IOException e ) {
       System.err.println( "I/O failed." );
     }
  }
}


class java.util.  Properties  
extends Hashtable<Object,Object>

gp  void load( InputStream inStream )
Lädt eine Properties-Liste aus einem Eingabestrom.
gp  void store( OutputStream out, String header )
Speichert die Properties-Liste mit Hilfe des Ausgabestroms ab. Am Kopf der Datei wird eine Kennung geschrieben, die im zweiten Argument steht. Die Kennung darf auch null sein.
gp  void Enumeration<?> propertyNames()
Liefert eine Enumeration von allen Schlüsseln in der Property-Liste inklusive der Standardwerte aus nachgeschalteten Properties.

Properties im XML-Format speichern

Seit Java 5 können die Properties auch im XML-Format gespeichert und geladen werden. Zum Speichern dient die Funktion storeXML() und zum Laden loadFromXML(). Die XML-Dateien haben ein spezielles Format, wie es ein Einzeiler wie System.getProperties().storeToXML(System.out, ""); zeigt.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd";>
<properties>
<comment></comment>
<entry key="java.runtime.name">Java(TM) 2 Runtime Environment, Standard Edition
</entry>
<entry key="sun.boot.library.path">C:\Programme\j2sdk1.5.0\jre\bin</entry>
<entry key="java.vm.version">1.5.0-beta-b32c</entry>
<entry key="java.vm.vendor">Sun Microsystems Inc.</entry>
<entry key="java.vendor.url">http://java.sun.com/</entry>
<entry key="path.separator">;</entry>
...
<entry key="sun.desktop">windows</entry>
<entry key="sun.cpu.isalist">pentium i486 i386</entry>
</properties>

Die Methode loadFormXML() liest aus einem InputStream und löst im Fall eines fehlerhaften Dateiformats eine InvalidPropertiesFormatException aus. Beim Speichern kann so ein Fehler natürlich nicht auftreten. Und genauso wie bei store() ein OutputStream mit einem Kommentar gespeichert wird, so tut das auch storeToXML(). Die Funktion ist mit einem zusätzlichen Parameter überladen, der eine XML-Kodierung erlaubt. Ist der Wert nicht gesetzt, so ist die Standardkodierung UTF-8.


Galileo Computing

11.7.6 Über die Beziehung Properties und Hashtable  toptop

Die Properties-Klasse ist eine Erweiterung von Hashtable, da die Speicherung der Einstellungsdaten in dieser Datenstruktur erfolgt. Ein Properties-Objekt erweitert die Hash-Tabelle um die Möglichkeit, die Schlüssel-Werte-Paare in einem festgelegten Format aus einer Textdatei beziehungsweise einem Datenstrom zu laden und wieder zu speichern. Doch gerade weil Hashtable erweitert wird, sind auch alle Methoden der Klasse Hashtable auf ein Properties-Objekt anwendbar. Das macht nicht für alle Methoden Sinn und ist auch nicht in jedem Fall problemlos. Dass Properties eine Unterklasse von Hashtable ist, ist ähnlich fragwürdig wie die Vererbungsbeziehung Stack als Unterklasse von Vector. So ist ein Augenmerk ist auf die put()-Funktion zu legen. Sie gibt es in der Klasse Properties nicht, denn put() wird von Hashtable geerbt. Wir sollten sie auch nicht verwenden, da es über sie möglich ist, Objekte einzufügen, die nicht vom Typ String sind. Das gleiche Argument könnte für get() gelten, doch zwei Dinge sprechen dagegen: Zunächst einmal, dass wir beim get() aus einem Hashtable-Objekt immer ein Object-Objekt bekommen und daher meistens eine Typanpassung benötigen, und zweitens durchsucht diese Methode lediglich den Inhalt des angesprochenen Properties-Exemplars. getProperties() arbeitet da etwas anders. Nicht nur, dass der Rückgabewert automatisch ein String ist, sondern auch, dass nachgeschaltete Properties-Objekte mit durchsucht werden, die zum Beispiel Standardwerte speichern.





Copyright © Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de