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?


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


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.


95

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

import java.beans.Introspector;

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

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 .


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


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


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.


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

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 .


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

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


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

Hasil:

res4: String = hello
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.