Bir String'in ilk karakterini küçük harf yapmanın en etkili yolu?


102

Küçük harfin ilk karakterini yapmanın en etkili yolu nedir String?

Bunu yapmanın birkaç yolunu düşünebilirim:

Kullanımı charAt()ilesubstring()

String input   = "SomeInputString";
String output  = Character.toLowerCase(input.charAt(0)) +
                   (input.length() > 1 ? input.substring(1) : "");

Veya bir chardizi kullanarak

 String input  = "SomeInputString";
 char c[]      = input.toCharArray();
 c[0]          = Character.toLowerCase(c[0]);
 String output = new String(c);

Bunu başarmanın birçok harika yolu olduğuna eminim. Ne önerirsiniz?


En iyi yol, mümkünse gereksinimlerinizi değiştirmektir. Bir String yerine bir StringBuilder'ı kabul edin ve onu doğrudan değiştirebilirsiniz.
Mark Peters

Bu bir cevap değil çünkü Java'nın dışında ve ASCII kodlamasına ve karakterin zaten alfabetik olduğunu bilmeye dayanıyor. Eski zamanların hacklemesi:c[0] |= ' ';
Mike Dunlavey


bu farklı bir soru
Andy

Yanıtlar:


127

Gelecek vaat eden yaklaşımları JMH kullanarak test ettim . Tam karşılaştırma kodu .

Testler sırasında varsayım (her seferinde köşe durumlarını kontrol etmekten kaçınmak için): giriş Dize uzunluğu her zaman 1'den büyüktür.

Sonuçlar

Benchmark           Mode  Cnt         Score        Error  Units
MyBenchmark.test1  thrpt   20  10463220.493 ± 288805.068  ops/s
MyBenchmark.test2  thrpt   20  14730158.709 ± 530444.444  ops/s
MyBenchmark.test3  thrpt   20  16079551.751 ±  56884.357  ops/s
MyBenchmark.test4  thrpt   20   9762578.446 ± 584316.582  ops/s
MyBenchmark.test5  thrpt   20   6093216.066 ± 180062.872  ops/s
MyBenchmark.test6  thrpt   20   2104102.578 ±  18705.805  ops/s

Skor saniye başına işlemdir, ne kadar çoksa o kadar iyidir.

Testler

  1. test1 ilk olarak Andy ve Hllink'in yaklaşımıydı:

    string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
    
  2. test2Andy'nin ikinci yaklaşımı oldu. Introspector.decapitalize()Daniel tarafından da önerildi, ancak iki ifaçıklama yapılmadı . İlk ifolarak test varsayımı nedeniyle kaldırıldı. İkincisi, doğruluğu ihlal ettiği için kaldırıldı (yani girdi "HI"geri dönecekti "HI"). Bu neredeyse en hızlıydı.

    char c[] = string.toCharArray();
    c[0] = Character.toLowerCase(c[0]);
    string = new String(c);
    
  3. test3bir modifikasyondu test2, ancak bunun yerine Character.toLowerCase()32 ekliyordum, bu sadece ve ancak dizge ASCII'de ise doğru çalışıyor. Bu en hızlıydı. c[0] |= ' 'Mike'ın yorumundan aynı performansı verdi.

    char c[] = string.toCharArray();
    c[0] += 32;
    string = new String(c);
    
  4. test4kullanılmış StringBuilder.

    StringBuilder sb = new StringBuilder(string);
    sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
    string = sb.toString();
    
  5. test5iki substring()çağrı kullandı .

    string = string.substring(0, 1).toLowerCase() + string.substring(1);
    
  6. test6char value[]doğrudan String'de değiştirmek için yansıma kullanır . Bu en yavaş olanıydı.

    try {
        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);
        char[] value = (char[]) field.get(string);
        value[0] = Character.toLowerCase(value[0]);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
    

Sonuçlar

Dize uzunluğu her zaman 0'dan büyükse, kullanın test2.

Değilse, köşe durumlarını kontrol etmeliyiz:

public static String decapitalize(String string) {
    if (string == null || string.length() == 0) {
        return string;
    }

    char c[] = string.toCharArray();
    c[0] = Character.toLowerCase(c[0]);

    return new String(c);
}

Metninizin her zaman ASCII'de olacağından eminseniz ve bu kodu darboğazda bulduğunuz için aşırı performans arıyorsanız, kullanın test3.


95

Üçüncü taraf bir kitaplık kullanmak istemiyorsanız güzel bir alternatifle karşılaştım:

import java.beans.Introspector;

Assert.assertEquals("someInputString", Introspector.decapitalize("SomeInputString"));

14
Bu yönteme ilişkin dokümandan: "Bu, normalde ilk karakteri büyük harften küçük harfe dönüştürmek anlamına gelir, ancak (olağandışı) özel durumda birden fazla karakter olduğunda ve hem birinci hem de ikinci karakterler büyük harf olduğunda, yalnız. "
Andy

1
Ayrıca, kaynağa bakıldığında, bu yöntem önceki yorumda anlattığım özel durumu ele aldığında, sorumda bahsettiğim gibi yalnızca char dizisini kullanır.
Andy

2
Tam olarak ihtiyacım olan şey. Introspector.decapitalize ("ABC") yine de ABC olacaktır. WordUtils.uncapitalize ("ABC") "aBC" üretir. İlkinin, baharın fasulyeleri nasıl otomatikleştirdiğini paylaştığımız için, ABCService'i fasulye adıyla almanız gerekiyorsa, bu aBCService değil, yine de ABCService.
köylü

21

String manipülasyonu söz konusu olduğunda Jakarta Commons Lang StringUtils'e bir göz atın .


8
Daha spesifik olarak, StringUtils kullanarak unapitalize (java.lang.String) yöntemi, kodunuzdaki NullPointerExceptions hakkında endişelenmenize gerek kalmaması gibi ek bir avantaja sahiptir.
hexium

3
Mutlaka en verimli değil, ama belki de en net olanı, ki bu çok önemli.
David Gelhar

2
Hangi kaynağı daha verimli hale getirdiğinize bağlıdır - CPU veya programcı zamanı :)
Dan Gravell

15

Apache Commons'ı kullanmak istiyorsanız, aşağıdakileri yapabilirsiniz:

import org.apache.commons.lang3.text.WordUtils;
[...] 
String s = "SomeString"; 
String firstLower = WordUtils.uncapitalize(s);

Sonuç: someString


3
Güzel ve temiz bir çözüm, ancak bu artık kullanımdan kaldırıldı, ortak metinleri kullanmalıyız:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
dk7

10

Karakter odaklı bir yaklaşıma rağmen, String odaklı bir çözüm öneririm. String.toLowerCase Yerel Ayara özeldir, bu nedenle bu sorunu dikkate alacağım. String.toLowerCasegöre düşük Kutulama için tercih etmektir Character.toLowerCase . Ayrıca, karakter odaklı bir çözüm tam unicode uyumlu değildir, çünkü Character.toLowerCase ek karakterleri işleyemez.

public static final String uncapitalize(final String originalStr,
            final Locale locale) {
        final int splitIndex = 1;
        final String result;
        if (originalStr.isEmpty()) {
        result = originalStr;
        } else {
        final String first = originalStr.substring(0, splitIndex).toLowerCase(
                locale);
        final String rest = originalStr.substring(splitIndex);
        final StringBuilder uncapStr = new StringBuilder(first).append(rest);
        result = uncapStr.toString();
        }
        return result;
    }

GÜNCELLEME: Yerel ayarın ne kadar önemli olduğuna bir örnek olarak I, Türkçe ve Almanca'da küçük harflerle yazalım :

System.out.println(uncapitalize("I", new Locale("TR","tr")));
System.out.println(uncapitalize("I", new Locale("DE","de")));

iki farklı sonuç çıkaracaktır:

ben

ben


7

Java'daki dizeler değişmezdir, bu nedenle her iki şekilde de yeni bir dizge oluşturulur.

İlk örneğiniz muhtemelen biraz daha verimli olacaktır çünkü geçici bir karakter dizisi değil, yalnızca yeni bir dizge oluşturması gerekir.


1
Aslında ilk yol, karakter dizisinden daha pahalı olan geçici bir String (alt dize için) oluşturur.
Hot Licks

1
Destek verileri olmadan yardımcı
olmuyor

3

İstediğinizi arşivlemek için çok kısa ve basit bir statik yöntem:

public static String decapitalizeString(String string) {
    return string == null || string.isEmpty() ? "" : Character.toLowerCase(string.charAt(0)) + string.substring(1);
}

2

İhtiyacınız olan şey çok basitse (örneğin, java sınıf adları, yerel ayar yok), Google Guava kitaplığındaki CaseFormat sınıfını da kullanabilirsiniz .

String converted = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, "FooBar");
assertEquals("fooBar", converted);

Veya daha verimli olabilecek bir dönüştürücü nesneyi hazırlayabilir ve yeniden kullanabilirsiniz.

Converter<String, String> converter=
    CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_CAMEL);

assertEquals("fooBar", converter.convert("FooBar"));

Google Guava dize işleme felsefesini daha iyi anlamak için bu wiki sayfasına göz atın .


1
String testString = "SomeInputString";
String firstLetter = testString.substring(0,1).toLowerCase();
String restLetters = testString.substring(1);
String resultString = firstLetter + restLetters;

1

Bununla karşılaştım ancak bugün. Bunu en yaya şekilde kendim yapmaya çalıştım. Bu bir satır aldı, tho longish. İşte gidiyor

String str = "TaxoRank"; 

System.out.println(" Before str = " + str); 

str = str.replaceFirst(str.substring(0,1), str.substring(0,1).toLowerCase());

System.out.println(" After str = " + str);

Verir:

Str = TaxoRanks'tan önce

Str = taxoRanks'tan sonra


1
val str = "Hello"
s"${str.head.toLower}${str.tail}"

Sonuç:

res4: String = hello
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.