Cara paling efisien untuk membuat karakter pertama dari huruf kecil String?

97

Apa cara paling efisien untuk membuat karakter pertama dari Stringhuruf kecil?

Saya dapat memikirkan sejumlah cara untuk melakukan ini:

Menggunakan charAt()dengansubstring()

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

Atau menggunakan chararray

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

Saya yakin ada banyak cara hebat lainnya untuk mencapai ini. Apa yang kamu sarankan?

Andy
sumber
Cara terbaik adalah mengubah kebutuhan Anda jika memungkinkan. Terima StringBuilder sebagai ganti String dan Anda dapat memodifikasinya secara langsung.
Mark Peters
Ini bukan jawaban karena di luar Jawa, dan bergantung pada pengkodean ASCII dan mengetahui bahwa karakter tersebut sudah alfabet. Ini adalah peretasan orang-orang lama:c[0] |= ' ';
Mike Dunlavey
kemungkinan duplikat Konversi ke huruf besar dan kecil di Jawa
Raedwald
itu pertanyaan yang berbeda
Andy

Jawaban:

123

Saya menguji pendekatan yang menjanjikan menggunakan JMH . Kode benchmark lengkap .

Asumsi selama pengujian (untuk menghindari pemeriksaan kasus sudut setiap saat): panjang string input selalu lebih besar dari 1.

Hasil

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

Skornya adalah operasi per detik, semakin banyak semakin baik.

Tes

  1. test1 adalah pendekatan Andy dan Hllink pertama:

    string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
  2. test2adalah pendekatan Andy kedua. Ini juga Introspector.decapitalize()dikemukakan oleh Daniel, tetapi tanpa dua ifpernyataan. Pertama ifdihapus karena asumsi pengujian. Yang kedua telah dihapus, karena melanggar kebenaran (yaitu, masukan "HI"akan kembali "HI"). Ini hampir yang tercepat.

    char c[] = string.toCharArray();
    c[0] = Character.toLowerCase(c[0]);
    string = new String(c);
  3. test3adalah modifikasi dari test2, tetapi alih-alih Character.toLowerCase(), saya menambahkan 32, yang berfungsi dengan benar jika dan hanya jika string ada dalam ASCII. Ini yang tercepat. c[0] |= ' 'dari komentar Mike memberikan penampilan yang sama.

    char c[] = string.toCharArray();
    c[0] += 32;
    string = new String(c);
  4. test4bekas StringBuilder.

    StringBuilder sb = new StringBuilder(string);
    sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
    string = sb.toString();
  5. test5menggunakan dua substring()panggilan.

    string = string.substring(0, 1).toLowerCase() + string.substring(1);
  6. test6menggunakan refleksi untuk mengubah char value[]secara langsung dalam String. Ini yang paling lambat.

    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();
    }

Kesimpulan

Jika panjang String selalu lebih besar dari 0, gunakan test2.

Jika tidak, kami harus memeriksa kasus sudut:

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);
}

Jika Anda yakin bahwa teks Anda akan selalu dalam ASCII dan Anda mencari kinerja ekstrim karena Anda menemukan kode ini di kemacetan, gunakan test3.

Adam Stelmaszczyk
sumber
95

Saya menemukan alternatif yang bagus jika Anda tidak ingin menggunakan perpustakaan pihak ketiga:

import java.beans.Introspector;

Assert.assertEquals("someInputString", Introspector.decapitalize("SomeInputString"));
Daniel Pacak
sumber
14
Dari dokumen untuk metode ini: "Ini biasanya berarti mengonversi karakter pertama dari huruf besar ke huruf kecil, tetapi dalam kasus khusus (tidak biasa) ketika ada lebih dari satu karakter dan kedua karakter pertama dan kedua adalah huruf besar, kita biarkan itu sendiri. "
Andy
1
Juga, melihat sumbernya, setelah metode ini menangani kasus khusus yang saya jelaskan di komentar sebelumnya, itu hanya menggunakan array karakter seperti yang saya sebutkan dalam pertanyaan saya.
Andy
2
Persis yang saya butuhkan. Introspector.decapitalize ("ABC") akan tetap menjadi ABC. WordUtils.uncapitalize ("ABC") menghasilkan "aBC". Hanya berbagi bahwa yang pertama adalah bagaimana spring melakukan autonaming kacang, jadi jika Anda perlu mengambil dengan nama kacang ABCService, itu bukan aBCService, tapi tetap ABCService.
penduduk desa
21

Untuk manipulasi string, lihat Jakarta Commons Lang StringUtils .

Carlos Tasada
sumber
8
Lebih khusus lagi, metode uncapitalize (java.lang.String) Menggunakan StringUtils memiliki keuntungan tambahan karena tidak perlu khawatir tentang NullPointerExceptions dalam kode Anda.
hexium
3
Belum tentu yang paling efisien, tapi mungkin yang paling jelas, yang sangat berarti.
David Gelhar
2
Tergantung sumber daya apa yang Anda buat lebih efisien - CPU atau waktu programmer :)
Dan Gravell
15

Jika Anda ingin menggunakan Apache Commons, Anda dapat melakukan hal berikut:

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

Hasil: someString

Sebastian
sumber
3
Ini solusi yang bagus dan bersih, tetapi ini sudah usang sekarang, kita harus menggunakan commons-text's:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
dk7
10

Meskipun pendekatan berorientasi char saya akan menyarankan solusi berorientasi String. String.toLowerCase spesifik untuk Lokal, jadi saya akan mempertimbangkan masalah ini. String.toLowerCaseadalah memilih huruf kecil menurut Character.toLowerCase . Juga solusi berorientasi karakter tidak kompatibel penuh unicode, karena Character.toLowerCase tidak dapat menangani karakter tambahan.

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;
    }

UPDATE: Sebagai contoh betapa pentingnya pengaturan lokal mari kita huruf kecil Idalam bahasa Turki dan Jerman:

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

akan menampilkan dua hasil berbeda:

saya

saya

Michael Konietzka
sumber
7

String di Java tidak dapat diubah, jadi string baru akan dibuat.

Contoh pertama Anda mungkin akan sedikit lebih efisien karena hanya perlu membuat string baru dan bukan array karakter sementara.

Alan Geleynse
sumber
1
Sebenarnya, cara pertama membuat String sementara (untuk substring), yang harganya lebih mahal daripada array karakter.
Hot Licks
1
Tidak membantu tanpa data pendukung
Nitsan Wakart
3

Metode statis yang sangat singkat dan sederhana untuk mengarsipkan apa yang Anda inginkan:

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

Jika yang Anda butuhkan sangat sederhana (mis. Nama kelas java, tanpa bahasa lokal), Anda juga dapat menggunakan kelas CaseFormat di pustaka Google Guava .

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

Atau Anda dapat menyiapkan dan menggunakan kembali objek konverter, yang bisa lebih efisien.

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

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

Untuk lebih memahami filosofi manipulasi string Google Guava, lihat halaman wiki ini .

Peter Lamberg
sumber
1
String testString = "SomeInputString";
String firstLetter = testString.substring(0,1).toLowerCase();
String restLetters = testString.substring(1);
String resultString = firstLetter + restLetters;
Bae Cheol Shin
sumber
1

Saya telah menemukan ini hanya hari ini. Mencoba melakukannya sendiri dengan cara paling pejalan kaki. Itu butuh satu baris, terlalu gondrong. Ini dia

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);

Memberikan:

Sebelum str = TaxoRanks

Setelah str = taxoRanks

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

Hasil:

res4: String = hello
Vivek
sumber