8. Karakterek és sztringek

8.1. A Character osztály

Egy Character típusú objektum egyetlen karakter értéket tartalmaz. A Character objektumot az egyszerű char változó helyett használjuk, amikor objektum szükséges, például: amikor átadunk egy karakter értéket egy metódusnak, ami megváltoztatja az értéket, vagy amikor egy karakter értéket helyezünk el egy adattárolóban, mint például egy ArrayList-ben, ami objektumokat tud csak tárolni, primitív értékeket nem.

Megjegyzés: a burkoló (csomagoló) osztályokról a következő fejezetben lesz szó.

A következő példaprogram (CharacterDemo) létrehoz néhány Character objektumot, és megjelenít róluk néhány információt. A Character osztályhoz tartozó kód az alábbiakban látható:

public class CharacterDemo {
    public static void main(String args[]) {
        Character a = new Character('a');
        Character a2 = new Character('a');
        Character b = new Character('b');

        int difference = a.compareTo(b);

        if (difference == 0) {
            System.out.println("a is equal to b.");
        } else if (difference < 0) {
            System.out.println("a is less than b.");
        } else if (difference > 0) {
            System.out.println("a is greater than b.");
        }
        System.out.println("a is "
            + ((a.equals(a2)) ? "equal" : "not equal")
            + " to a2.");
        System.out.println("The character " + a.toString()
            + " is " + (Character.isUpperCase(a.charValue()) ?
                 "upper" : "lower")
            + "case.");
    }
}

A program kimenete a következő lesz:

a is less than b.
a is equal to a2.
The character a is lowercase.

A fenti példában a Character.isUpperCase(a.charValue()) függvény adja az a nevű Character objektum kódját. Ez azért van, mert az isUppercase metódus char típusú paramétert vár. Ha a JDK 5.0-t vagy újabb fejlesztőkörnyezetet használunk, akkor ennek a metódusnak megadhatjuk a Character típusú objektumot is:

Character.isUpperCase(a)

A CharacterDemo program a Character osztály alábbi konstruktorait, illetve metódusait hívja meg:

  • Character(char): A Character osztály egyetlen konstruktora, amely létrehoz egy Character objektumot, melynek értékét a paraméterben adjuk meg, létrehozás után a Character objektum értéke nem változhat.
  • compareTo(Character): Összehasonlít két Character objektumban tárolt értéket, azt az objektumot, ami meghívta (a példában a), és azt, ami a paraméterben van (b). Visszaad egy egész számot, ami jelzi, hogy az objektum értéke kisebb, egyenlő, vagy nagyobb, mint a paraméterben megadott érték.
  • equals(Object): 2 Character objektumot hasonlít össze. True értékkel tér vissza, ha a két érték egyenlő.
  • toString(): Stringgé konvertálja az objektumot, a sztring 1 karakter hosszú lesz, és a Character objektum értékét tartalmazza.
  • charValue(): Megadja az objektum értékét egyszerű char értékként.
  • isUpperCase(char): Meghatározza, hogy az egyszerű char érték nagybetű-e.

A Character osztály néhány további fontosabb tagfüggvénye :

  • boolean isUpperCase(char)
  • boolean isLowerCase(char)
  • char toUpperCase(char)
  • char toLowerCase(char)
  • boolean isLetter(char)
  • boolean isDigit(char)
  • boolean isLetterOrDigit(char)
  • boolean isWhitespace(char)
  • boolean isSpaceChar(char)
  • boolean isJavaIdentifierStart(char)
  • boolean isJavaIdentifierPart(char)

8.2. String, StringBuffer és StringBuilder osztály

A Java platform a kezdetektől fogva biztosított két osztályt, melyekkel tárolhatunk, illetve manipulálhatunk sztringeket, ezek a String és a StringBuffer. A String osztályban olyan sztringeket tárolunk, melyek értéke nem fog változni. A StringBuffer osztályt akkor használjuk, ha a szövegen szeretnénk változtatni, ezt elsősorban dinamikus karakterlánc készítésekor (pl. fájlból olvasás) használjuk. A StringBuffer-ek használata biztonságos több szálas környezetben. A StringBuilder osztályt a JDK 5.0-tól vezették be, ami gyorsabb, mint a StringBuffer, de csak egy szálon használható biztonságosan.

A következő irányelvek alapján döntsünk, hogy melyik osztályt használjuk:

  • Ha a szöveg nem fog változni, használjuk a String-et.
  • Ha a szöveg változni fog, és csak egy szálon keresztül fogjuk elérni, használjuk a StringBuilder-t.
  • Ha a szöveg változni fog és több szálon keresztül fogjuk elérni StringBuffer-t használjuk.

A következő példaprogram neve StringsDemo, amely megfordítja egy sztring karaktereit. A program használja a String és StringBuilder osztályokat is. Ha a JDK 5.0-ás változatánál régebbit használ, a StringBuilder előfordulásait le kell cserélni StringBuffer-re, és a program működni fog.

public class StringsDemo {
    public static void main(String[] args) {
        String palindrome = "Dot saw I was Tod";
        int len = palindrome.length();
        StringBuilder dest = new StringBuilder(len);
        for (int i = (len - 1); i >= 0; i--) {
            dest.append(palindrome.charAt(i));
        }
        System.out.println(dest.toString());
    }
}

A program kimenete a következő lesz:

doT saw I was toD

Megjegyzés: Érdemes még a példán megfigyelni, hogy a StringBuilder (és StringBuffer) osztály példányosításakor az előre látható méretet meg lehet adni. Ez gyorsabb futást eredményez.

8.2.1. Sztring objektumok létrehozása

A sztringet gyakran egy sztring konstansból, egy karaktersorozatból készítjük. A StringsDemo program is ezt a módszert használja, hogy létrehozzon egy sztringet, amire a palindrome változóval hivatkozik:

String palindrome = "Dot saw I was Tod";

String-eket úgy is előállíthatunk, min bármilyen más Java objektumot: a new kulcsszó és a konstruktor segítségével. A String osztály több konstruktort szolgáltat, amelyekkel beállíthatjuk a String kezdőértékét, különböző forrásokat használva, mint például karakter tömböt, byte tömböt, StringBuffert, vagy StringBuildert.

A következő lista a String osztály konstruktorait tartalmazza:

String() Üres String-et hoz létre.
String(byte[]) String(byte[], int, int) String(byte[], int, int, String) String(byte[], String) Bájttömb tartalma alapján jön létre. Lehetőség van egy egész kezdőindex és hossz megadására részintervallum figyelembevételéhez, illetve meg lehet adni karakterkódolást is.
String(char[]) String(char[], int, int) Karaktertömb egésze vagy csak egy része alapján jön létre.
String(String) Másik String másolatát hozza létre.
String(StringBuffer) StringBuffer tartalma alapján jön létre.
String(StringBuilder) StringBuilder tartalma alapján jön létre.

Egy példa arra, amikor karaktertömbből készítünk sztringet:

char[] helloArray = { 'h', 'e', 'l', 'l', 'o' };
String helloString = new String(helloArray);
System.out.println(helloString);

A kódrészlet utolsó sora ezt írja ki:

hello

Ha StringBuffer-t, vagy StringBuilder-t hozunk létre, mindig használnunk kell a new operátort. Mivel a két osztálynak azonosak a konstruktorai, a következő lista csak a StringBuffer osztály konstruktorait tartalmazza:

  • StringBuffer()
  • StringBuffer(CharSequence)
  • StringBuffer(int)
  • StringBuffer(String)

A StringsDemo programban egy dest nevű StringBuildert hozunk létre, azt a konstruktort használva, amely a puffer kapacitását állítja be:

String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
StringBuilder dest = new StringBuilder(len);

A kód létrehoz egy StringBuildert, melynek kezdeti kapacitása megegyezik a palindrome nevű String hosszával. Ez csak a memóriafoglalást biztosítja a dest számára, mert ennek mérete pontosan elegendő lesz a bemásolandó karakterek számára. A StringBuffer vagy StringBuilder kapacitásának inicializálásával egy elfogadható méretre minimalizálhatjuk a memóriafoglalások számát, amivel sokkal hatékonyabbá tehetjük programunkat, mert a memóriafoglalás elég költséges művelet.

Megjegyzés: Ha egy StringBuilder vagy StringBuffer objektum méret növelése során a szabad kapacitása elfogy, akkor egy új, kétszer akkora memóriaterület kerül lefoglalásra, ahová a régi tartalom átmásolásra kerül. Ebből is látszik, hogy ha tudjuk, érdemes pontosan (vagy legalább becsülten) megadni a szükséges kapacitást.

8.2.2. A Stringek hossza

Azon metódusokat, amelyeket arra használunk, hogy információt szerezzünk egy objektumról, olvasó (vagy hozzáférő) metódusoknak nevezzük. Egy ilyen String-eknél, StringBuffer-eknék és StringBuilder-eknél használható metódus a length, amely visszaadja az objektumban tárolt karakterek számát. Miután az alábbi két sor végrehajtódik, a len változó értéke 17 lesz:

String palindrome = "Dot saw I was Tod";
int len = palindrome.length();

A length mellett StringBuffer és StringBuilder osztályok esetén használhatjuk még a capacity metódust is, amely a lefoglalt terület méretét adja vissza, és nem a használt területet. Például a dest nevű StringBuilder kapacitása a StringDemo programban nem változik, míg a hossza minden karakter beolvasása után nő egyel. A következő ábra mutatja a dest kapacitását és hosszát, miután kilenc karakter hozzá lett fűzve.

StringBuilder kapacitás

A StringBuffer vagy StringBuilder hossza a benne tárolt karakterek száma, míg kapacitása az előre lefoglalt karakterhelyek száma. A String osztály esetén a capacity metódus nem használható, mert a String tartalma nem változhat meg.

8.2.3. Stringek karaktereinek olvasása

A megfelelő indexű karaktert megkapjuk a String-en, StringBuffer-en vagy a StringBuilder-en belül, ha meghívjuk a charAt függvényt. Az első karakter indexe 0, az utolsó karakteré pedig a length()-1.

Például az alábbi forráskódban a 9. indexű karaktert kapjuk meg a String-ben:

String anotherPalindrome = "Niagara. O roar again!";
char aChar = anotherPalindrome.charAt(9);

Az indexelés 0-val kezdődik, tehát a 9-es indexű karakter az ’0’, mint ahogy a következő ábra is mutatja:

charAt

Használjuk a charAt függvényt, hogy megkapjuk a megfelelő indexű karaktert.

Az ábra is mutatja, hogy hogyan lehet kiszámítani egy String-ben az utolsó karakter indexét. Ki kell vonni a length() függvény visszatérési értékéből 1-et.

Ha több, mint egy karaktert szeretnénk megkapni a String-ből, StringBuffer-ből vagy StringBuilder-ből, akkor a substring függvényt kell használni. A substring-nek két fajtája van, amit az alábbi táblázat is mutat:

  • String substring(int)
  • String substring(int, int)

Visszatérési érték egy új String, ami az eredeti sztring részének másolata. Az első paraméter az első karakter indexe (ahonnan kérjük a karaktereket), a második int paraméter pedig az utolsó karakter indexe (ameddig kérjük)-1. A substring hosszát megkapjuk, ha a második paraméter értékből kivonjuk az első paraméter értékét. Ha nem adjuk meg a második paramétert, akkor az eredeti String végéig történik a másolás.

Az alábbi forráskód a Niagara tükörmondatból ad vissza egy részletet, ami a 11. indextől a 15. indexig tart, ez pedig a „roar” kifejezés:

String anotherPalindrome = "Niagara. O roar again!";
String roar = anotherPalindrome.substring(11, 15);

substring

8.2.4. Karakter vagy String keresése Stringben

A String osztály két függvényt nyújt, amelyek pozícióval térnek vissza: indexOf és a lastIndexOf. A következő lista e két függvény alakjait mutatja be:

int indexOf(int), int lastIndexOf(int) Visszaadja az első (utolsó) előforduló karakter indexét.
int indexOf(int, int), int lastIndexOf(int, int) Visszaadja az első (utolsó) előfordulókarakter indexét, az indextől előre (visszafele) keresve.
int indexOf(String), int lastIndexOf(String) Visszaadja az első (utolsó) előforduló String indexét.
int indexOf(String, int), int lastIndexOf(String, int) Visszaadja a String első (utolsó) előfordulásának indexét, a megadott indextől előre (visszafele) keresve.

A StringBuffer és a StringBuilder osztályok nem támogatják az indexOf és a lastIndexOf függvényeket. Ha használni szeretnénk ezeket a metódusokat, először String-gé kell konvertálnunk az objektumot a toString függvény segítségével.

Megjegyzés: A metódusok az alábbi Filename osztályban nem minden hibát kezelnek, és feltételezik, hogy az paraméter tartalmazza a teljes könyvtár útvonalat, és a fájlnevet kiterjesztéssel.

public class Filename {
    private String fullPath;
    private char pathSeparator, extensionSeparator;
    public Filename(String str, char sep, char ext) {
        fullPath = str;
        pathSeparator = sep;
        extensionSeparator = ext;
    }
    public String extension() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        return fullPath.substring(dot + 1);
    }
    public String filename() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(sep + 1, dot);
    }
    public String path() {
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(0, sep);
    }
}

A következő program létrehoz egy Filename objektumot, és meghívja a metódusait:

public class FilenameDemo {
    public static void main(String[] args) {
        final String FPATH = "/home/mem/index.html";
        Filename myHomePage = new Filename(FPATH,
                                           '/', '.');
        System.out.println("Extension = " +
             myHomePage.extension());
        System.out.println("Filename = " +
             myHomePage.filename());
        System.out.println("Path = " +
             myHomePage.path());
    }
}

A program kimenete:

Extension = html
Filename = index
Path = /home/mem

Ahogy a fenti példa is mutatja, az extension metódus a lastIndexOf függvényt használja, hogy megtalálja az utolsó pontot a fájlnévben. Ha a fájlnévben nincs pont, a lastIndexOf visszatérési értéke -1, és a substring metódus StringIndexOutOfBoundsException kivételt dob.

Ha a pont karakter (.) az utolsó karakter a String-ben, akkor ez egyenlő a String hosszával, ami egyel nagyobb, mint a legnagyobb index a String-ben (mivel az indexelés 0-val kezdődik).

8.2.5. Sztringek és rész-sztringek összehasonlítása

A String osztálynak van néhány függvénye a sztringek és a rész-sztringek összehasonlítására. Az alábbi lista ezeket a függvényeket mutatja be:

boolean endsWith(String), boolean startsWith(String), boolean startsWith(String, int) Visszatérési értéke igaz, ha a String a paraméterben megadott szóval kezdődik, vagy végződik. Az int paraméterben az eltolási értéket adhatjuk meg, hogy az eredeti String-ben hanyadik indextől kezdődjön a keresés.

int compareTo(String), int compareTo(Object), int compareToIgnoreCase(String) Két String-et hasonlít össze ABC szerint, és egy egész számmal tér vissza, jelezve, hogy ez a String nagyobb (eredmény>0), egyenlő (eredmény=0), illetve kisebb (eredmény<0), mint a paraméter. A compareToIgnoreCase nem tesz különbséget a kis-és nagybetűk között.

boolean equals(Object), boolean equalsIgnoreCase(String) Visszatérési értéke igaz, ha a String ugyanazt a karaktersorozatot tartalmazza, mint a paramétere. Az equalsIgnoreCase függvény nem tesz különbséget kis- és nagybetűk között; így ’a’ és ’A’ egyenlő.

8.2.6. Sztringek módosítása

A String osztály sokféle metódust tartalmaz a String-ek módosításához. Természetesen a String objektumokat nem tudja módosítani, ezek a metódusok egy másik String-et hoznak létre, ez tartalmazza a változtatásokat. Ezt követhetjük az alábbi listában.

  • String concat(String) A String végéhez láncolja a String paramétert. Ha az paraméter hossza 0, akkor az eredeti String objektumot adja vissza.
  • String replace(char, char) Felcseréli az összes első paraméterként megadott karaktert a második paraméterben megadottra. Ha nincs szükség cserére, akkor az eredeti String objektumot adja vissza.
  • String trim() Eltávolítja az elválasztó karaktereket a String elejéről és a végéről.
  • String toLowerCase() String toUpperCase() Konvertálja a String-et kis, vagy nagybetűsre. Ha nincs szükség konverzióra, az eredeti String-et adja vissza.

Íme egy rövid program (BostonAccentDemo), ami a replace metódussal egy String-et fordít Bostoni dialektusra:

public class BostonAccentDemo {
    private static void bostonAccent(String sentence) {
        char r = 'r';
        char h = 'h';
        String translatedSentence = sentence.replace(r, h);
        System.out.println(translatedSentence);
    }
    public static void main(String[] args) {
        String translateThis =
            "Park the car in Harvard yard.";
        bostonAccent(translateThis);
    }
}

A replace metódus kicseréli az összes r-t h-ra a mondatokban.

A program kimenete:

Pahk the cah in Hahvahd yahd.

8.2.7. A StringBuffer-ek módosítása

A StringBuffer-ek tartalma módosítható. A következő lista összefoglalja a StringBuffer-ek módosításához használható metódusokat. Azonos metódusokat tartalmaz a StringBuilder osztály is, de StringBuilder-eket is ad vissza, ezért ezeket külön nem soroljuk fel.

  • StringBuffer append(boolean)
  • StringBuffer append(char)
  • StringBuffer append(char[])
  • StringBuffer append(char[], int, int)
  • StringBuffer append(double)
  • StringBuffer append(float)
  • StringBuffer append(int)
  • StringBuffer append(long)
  • StringBuffer append(Object)
  • StringBuffer append(String)

Hozzáfűzi a megadott paramétert a StringBuffer-hez. Az adat String-gé konvertálódik, mielőtt a hozzáfűzés megtörténne.

  • StringBuffer delete(int, int)
  • StringBuffer deleteCharAt(int)

Törli a megadott karaktereket a StringBuffer-ből.

  • StringBuffer insert(int, boolean)
  • StringBuffer insert(int, char)
  • StringBuffer insert(int, char[])
  • StringBuffer insert(int, char[], int, int)
  • StringBuffer insert(int, double)
  • StringBuffer insert(int, float)
  • StringBuffer insert(int, int)
  • StringBuffer insert(int, long)
  • StringBuffer insert(int, Object)
  • StringBuffer insert(int, String)

A StringBuffer-hez ad egy új paramétert. Az első egész típusú paraméter jelzi az adat indexét, ahova a beillesztés történik. Az adat String-gé konvertálódik, mielőtt a beillesztés megtörténik.

  • StringBuffer replace(int, int, String)
  • void setCharAt(int, char)

Kicseréli a megadott karaktereket a StringBuffer-ben.

  • StringBuffer reverse()

Felcseréli a karakterek sorrendjét a StringBuffer-ben.

Az append metódusra már láttunk példát a StringsDemo programban, a fejezet elején. Az alábbi InsertDemo program bemutatja az insert metódus használatát. Beilleszt egy String-et a StringBuffer-be:

public class InsertDemo {
    public static void main(String[] args) {
        StringBuffer palindrome = new StringBuffer(
            "A man, a plan, a canal; Panama.");
        palindrome.insert(15, "a cat, ");
    System.out.println(palindrome);
    }
}

A program kimenete:

A man, a plan, a cat, a canal; Panama.

Az általunk megadott index utáni helyre kerül beillesztésre az adat. A StringBuffer elejére való beillesztéshez a 0 indexet kell használni. A végéhez való beillesztés esetén az index értéke megegyezik a StringBuffer jelenlegi hosszával, vagy használjuk a hozzáfűzést (append) is.

Ha a művelet miatt túlságosan megnő a StringBuffer mérete, akkor az több memóriát fog lefoglalni. Mivel a memória lefoglalás költséges, ezért lehetőleg úgy kell elkészíteni a kódot, hogy megfelelően be legyen állítva a StringBuffer mérete.

8.2.8. A String-ek és a fordító

A fordító felhasználja a String és a StringBuffer osztályokat a háttérben, hogy a String-literálokat és különböző összefűzéseket kezelje. A String-et idézőjelek között adhatjuk meg:

"Hello World!"

String-literálokat bárhol használhatunk String példányként. Példaként, a System.out.println paraméterének String-literált adunk meg:

System.out.println("Might I add that you look lovely today.");

Használhatunk String metódust közvetlenül a String-literálból hívva:

int len = "Goodbye Cruel World".length();

Használhatjuk a String-literált String inicializálásra:

String s = "Hola Mundo";

A következő példa egyenértékű az előzővel, de nem olyan hatékony. Két azonos String-et készít, használata kerülendő:

String s = new String("Hola Mundo"); //ne használjuk

Használható a + operátor a String-ek összefűzésére:

String cat = "cat";
System.out.println("con" + cat + "enation");

Az előző példa alapján a fordító a StringBuffer-eket használja az összefűzés végrehajtására:

String cat = "cat";
System.out.println(new StringBuffer().append("con").
append(cat).append("enation").toString());

Használható a + operátor az összefűzésre:

System.out.println("You're number " + 1);

A fordító konvertálja a nem String értéket (a példában int 1-et) String objektummá, mielőtt az összefűzést elvégzi.

8.3. Sztringek darabolása

A java.util.StringTokenizer osztály hasznos lehet, ha egy String-et adott elválasztó karakter(ek) mentén szét kell bontani. A következő egyszerű példa bemutatja a használat módját:

StringTokenizer st = new StringTokenizer("this is a test");
while (st.hasMoreTokens()) {
    System.out.println(st.nextToken());
}

A kód a következő eredményt írja ki:

this
is
a
test

A StringTokenizer objektum nyilván tartja, hogy a feldolgozás a String melyik pontján jár. A konstruktornak megadhatunk a szövegen kívül egy elválasztó-karaktereket tartalmazó String-et is, ekkor az alapértelmezett "\t\n\r\f" elválasztók helyett ezt fogja az objektum figyelembe venni.

8.4. Ellenőrző kérdések

  • Mi a karakter?
  • Hányféle jelet képes tárolni a Java char típusa?
  • Hogy hívják a Java által támogatott karaktertípust?
  • Mi a karaktersorozat (sztring?)
  • Mit jelent, hogy a String nem megváltoztatható?
  • Hogyan lehet egy String-nek kezdőértéket adni?
  • Mire való a String indexOf metódusa?
  • Mire való String substring metódusa?
  • Mi a különbség a StringBuilder, a StringBuffer és a String között?

Melyik kifejezés értéke lesz logikailag igaz?

  • "john" == "john"
  • "john".equals("john")
  • "john" = "john"
  • "john".equals(new Button("john"))

Melyik fordul le?

  • "john" + " was " + " here"
  • "john" + 3
  • 3 + 5
  • 5 + 5.5

Mit ír ki a következő kódrészlet?

String s = new String("Bicycle");
int iBegin=1;
char iEnd=3;
System.out.println(s.substring(iBegin, iEnd));
  • Bic
  • ic
  • icy
  • fordítási hiba miatt nem indul el

Ha a ”Java” tartalmú s String-ben keressük a ’v’ betű pozícióját (a 2-t), akkor melyik metódushívással kapjuk ezt meg?

  • mid(2,s);
  • s.charAt(2);
  • s.indexOf('v');
  • indexOf(s,'v');

A következő deklarációk esetén melyik művelet érvényes?

String s1 = new String("Hello")
String s2 = new String("there");
String s3 = new String();
  • s3=s1 + s2;
  • s3=s1 - s2;
  • s3=s1 & s2;
  • s3=s1 && s2