Tarayıcı vs. StringTokenizer vs. String.Split


155

Ben sadece Java'nın Tarayıcı sınıfı hakkında öğrendim ve şimdi nasıl karşılaştırır / StringTokenizer ve String.Split ile rekabet merak ediyorum. StringTokenizer ve String.Split sadece Dizeleri üzerinde çalıştığını biliyorum, bu yüzden neden bir String için Tarayıcı kullanmak isteyeyim? Tarayıcı sadece ayırma için tek elden alışveriş mi olacak?

Yanıtlar:


240

Aslında kurslar için atlar.

  • Scannerbir dizeyi ayrıştırmanız ve farklı türdeki verileri çıkarmanız gereken durumlar için tasarlanmıştır. Çok esnektir, ancak tartışmalı bir şekilde, belirli bir ifadeyle sınırlandırılmış bir dizi dizeyi almak için size en basit API'yi vermez.
  • String.split()ve Pattern.split()ikincisini yapmak için size kolay bir sözdizimi sağlar, ancak temelde yaptıkları budur. Ortaya çıkan dizeleri ayrıştırmak veya belirli bir jetona bağlı olarak sınırlayıcıyı yarıya kadar değiştirmek isterseniz, size bu konuda yardımcı olmazlar.
  • StringTokenizerkullanmaktan daha kısıtlayıcı String.split()ve aynı zamanda biraz daha tutkulu. Temel olarak sabit alt dizelerle sınırlandırılmış belirteçleri çıkarmak için tasarlanmıştır. Bu kısıtlama nedeniyle, yaklaşık iki kat daha hızlıdır String.split(). (Benim Bkz karşılaştırmasını String.split()veStringTokenizer .) Aynı zamanda bunların düzenli ifadeler API, öncedir String.split()bir parçasıdır.

String.split()Zamanlamalarımdan , tipik bir makinede birkaç milisaniyede binlerce dizgiyi hala token edebileceklerini not edeceksiniz . Buna ek olarak, StringTokenizersize bir dize dizisi olarak çıkış vermesi avantajına sahiptir , bu genellikle istediğiniz şeydir. Bir Enumerationtarafından sağlanan StringTokenizer, çoğu zaman çok "sözdizimsel telaşlı" kullanmaktır. Bu açıdan bakıldığında, StringTokenizergünümüzde biraz yer kaybı ve sadece kullanabilirsiniz String.split().


8
Ayrıca, String.Split ve StringTokenizer üzerinde çalıştırdığınız aynı testlerde Scanner'ın sonuçlarını görmek de ilginç olacaktır.
Dave

2
Bana başka bir soruya cevap verdi: "Java API notlarında belirtildiği gibi, StringTokenizer kullanımı neden önerilmez?". Bu metinden cevabın "String.split () yeterince hızlı olduğu için" olduğu anlaşılıyor.
Bacaklar

1
StringTokenizer şu anda hemen hemen kullanılmıyor mu?
Yapımcı Steve

onun yerine ne kullanılır? Tarayıcı?
Adrian

4
Bunun eski bir sorunun cevabı olduğunu anlıyorum, ancak büyük bir metin akışını anında jetonlara bölmem gerekirse, StringTokenizerhala en iyi bahisim değil, çünkü String.split()sadece bellek tükenecek mi?
Sergei Tachenov

57

Elimine ederek başlayalım StringTokenizer. Yaşlanıyor ve düzenli ifadeleri bile desteklemiyor. Belgelerinde şunlar belirtiliyor:

StringTokenizerkullanımı yeni kodda kullanılmamasına rağmen uyumluluk nedeniyle saklanan eski bir sınıftır. Bu işlevselliği arayan herkesin bunun yerine veya splityöntemini kullanması önerilir .Stringjava.util.regex

Öyleyse hemen atalım. Bırakıyor split()ve Scanner. Aralarındaki fark nedir?

Birincisi, split()foreach döngüsü kullanmayı kolaylaştıran bir dizi döndürür:

for (String token : input.split("\\s+") { ... }

Scanner daha çok bir akış gibi oluşturulmuştur:

while (myScanner.hasNext()) {
    String token = myScanner.next();
    ...
}

veya

while (myScanner.hasNextDouble()) {
    double token = myScanner.nextDouble();
    ...
}

(Oldukça büyük bir API'ye sahip , bu yüzden her zaman böyle basit şeylerle sınırlı olduğunu düşünmeyin.)

Bu akış tarzı arayüz, ayrıştırmaya başlamadan önce tüm girdilere sahip olmadığınız (veya alamadığınız) basit metin dosyalarını veya konsol girişini ayrıştırmak için yararlı olabilir.

Şahsen, hatırlayabildiğim tek zaman Scannerokul projeleri için, komut satırından kullanıcı girişi almak zorunda kaldığım zamandır . Bu tür işlemleri kolaylaştırır. Ama eğer Stringbölmek istediğim bir şey varsa , bu neredeyse devam etmek için beyinsizdir split().


20
StringTokenizer, String.split () 'den 2 kat daha hızlıdır. Normal ifadeler kullanmanız gerekmiyorsa YAPMAYIN!
Alex Worden

Ben sadece Scannerbelirli bir satırda yeni çizgi karakterleri tespit etmek için kullanılır String. Yeni satır karakterleri platformdan platforma değişebildiğinden ( Patternjavadoc! 'A bakın ) ve giriş dizesinin uyması garanti EDİLMEDİĞİNDEN System.lineSeparator(), Scannerarama yaparken hangi yeni satır karakterlerinin aranacağını zaten bildiği için daha uygun buluyorum nextLine(). Çünkü String.splitherhangi bir standart konumda depolanmış bulamadığım çizgi ayırıcıları tespit etmek için doğru regex desenini beslemek zorunda kalacağım (yapabileceğim en iyi şey Scannersınıfın kaynağından kopyalamaktır ).
ADTC

9

StringTokenizer her zaman oradaydı. En hızlısıdır, ancak numaralandırma benzeri deyim diğerleri kadar zarif görünmeyebilir.

JDK 1.4'te bölünme gerçekleşti. Tokenizer'dan daha yavaş ancak String sınıfından çağrılabildiğinden kullanımı daha kolaydır.

Tarayıcı JDK 1.5'te geldi. En esnek olanıdır ve ünlü Cs scanf işlev ailesinin eşdeğerini desteklemek için Java API'sindeki uzun süreli bir boşluğu doldurur.


6

Jetonlamak istediğiniz bir String nesneniz varsa, String'in split yöntemini bir StringTokenizer üzerinden kullanmayı tercih edin . Programın dışındaki bir kaynaktan, örneğin bir dosyadan veya kullanıcıdan gelen metin verilerini ayrıştırıyorsanız, Tarayıcı burada işe yarar.


5
Aynen böyle, gerekçe yok, sebep yok mu?
jan.supol

6

Bölme yavaş, ancak Tarayıcı kadar yavaş değil. StringTokenizer bölünmekten daha hızlıdır. Ancak, JFastParser'da yaptığım bir hız artışı elde etmek için biraz esneklikle ticaret yaparak iki kat hız elde edebileceğimi buldum https://github.com/hughperkins/jfastparser

Bir milyon iki katına sahip bir ipte test:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms

Bazı Javadoc iyi olurdu ve sayısal verilerden başka bir şeyi ayrıştırmak isterseniz ne olur?
NickJ

Güzellik için değil hız için tasarlandı. Oldukça basit, sadece birkaç satır, böylece isterseniz metin ayrıştırma için birkaç seçenek daha ekleyebilirsiniz.
Hugh Perkins

4

String.split, StringTokenizer'dan çok daha yavaş görünüyor. Bölmenin tek avantajı, bir dizi belirteç almanızdır. Ayrıca düzenli ifadeleri bölünmüş olarak kullanabilirsiniz. org.apache.commons.lang.StringUtils, iki vizden çok daha hızlı çalışan bir bölme yöntemine sahiptir. StringTokenizer veya String.split. Ancak her üçü için CPU kullanımı neredeyse aynı. Bu yüzden, daha az CPU yoğun olan ve hala bulamadığım bir yönteme ihtiyacımız var.


3
Bu cevap biraz saçma. Daha hızlı ancak "daha az CPU yoğun" bir şey aradığınızı söylüyorsunuz. Herhangi bir program CPU tarafından yürütülür. Bir program CPU'nuzu% 100 kullanmıyorsa, G / Ç gibi başka bir şey beklemelidir. Doğrudan disk erişimi yapmadığınız sürece (özellikle burada yapmadığımız), dize belirteçlerini tartışırken bu bir sorun olmamalıdır.
Jolta

4

Son zamanlarda yüksek performans duyarlı durumlarda String.split () kötü performans hakkında bazı deneyler yaptım. Bunu faydalı bulabilirsiniz.

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

Amaç, String.split () yönteminin her seferinde Düzenli İfade deseni derlemesi ve böylece önceden derlenmiş bir Pattern nesnesi kullanmanız ve bunu doğrudan bir String'de çalıştırmak için kullanmanızla karşılaştırıldığında programınızı yavaşlatabilmesidir.


4
Aslında String.split () her zaman kalıbı derlemez. 1.7 java ise kaynağa bakın, desenin tek bir karakter olup olmadığını ve kaçan bir karakter olup olmadığını kontrol edin, dizeyi regexp olmadan böler, bu yüzden oldukça hızlı olmalıdır.
Krzysztof Krasoń

1

Varsayılan senaryolar için de Pattern.split () öneririm, ancak maksimum performansa ihtiyacınız varsa (özellikle Android'de test ettiğim tüm çözümler oldukça yavaştır) ve sadece tek bir karakterle bölmeniz gerekiyor, şimdi kendi yöntemimi kullanıyorum:

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    final int length = s.length;
    int offset = 0;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == splitChar) {
            if (count > 0) {
                result.add(new String(s, offset, count));
            }
            offset = i + 1;
            count = 0;
        } else {
            count++;
        }
    }
    if (count > 0) {
        result.add(new String(s, offset, count));
    }
    return result;
}

Bir String karakter dizisini almak için "abc" .toCharArray () kullanın. Örneğin:

String s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');

1

Önemli bir fark, hem String.split () hem de Scanner'ın boş dizeler üretebilmesidir, ancak StringTokenizer bunu asla yapmaz.

Örneğin:

String str = "ab cd  ef";

StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());

String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);

Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());

Çıktı:

//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2: 
#3: ef
//Scanner
#0: ab
#1: cd
#2: 
#3: ef

Bunun nedeni String.split () ve Scanner.useDelimiter () için sınırlayıcının yalnızca bir dize değil, normal bir ifade olmasıdır. StringTokenizer gibi davranmaları için yukarıdaki örnekte "" sınırlayıcısını "+" ile değiştirebiliriz.


-5

String.split () çok iyi çalışır ancak kendi sınırlarına sahiptir, örneğin bir dizeyi tek veya çift boru (|) sembolüne göre aşağıda gösterildiği gibi bölmek isterseniz, işe yaramaz. Bu durumda StringTokenizer kullanabilirsiniz.

ABC | IJK


12
Aslında, örneğinizi sadece "ABC | IJK" .split ("\\ |") ile bölebilirsiniz;
Tomo

"ABC || DEF ||" .split ("\\ |") gerçekten işe yaramaz çünkü sondaki iki boş değeri yoksayar;
Armand
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.