วิธีที่มีประสิทธิภาพที่สุดในการสร้างอักขระตัวแรกของ String ตัวพิมพ์เล็ก?


102

วิธีใดที่มีประสิทธิภาพที่สุดในการสร้างอักขระตัวแรกของตัวStringพิมพ์เล็ก

ฉันคิดได้หลายวิธีในการทำสิ่งนี้:

ใช้charAt()กับsubstring()

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

หรือใช้charอาร์เรย์

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

ฉันแน่ใจว่ามีวิธีที่ยอดเยี่ยมอื่น ๆ อีกมากมายในการบรรลุเป้าหมายนี้ คุณแนะนำเมนูใด?


วิธีที่ดีที่สุดคือเปลี่ยนความต้องการของคุณถ้าเป็นไปได้ ยอมรับ StringBuilder แทน String และคุณสามารถแก้ไขได้โดยตรง
Mark Peters

นี่ไม่ใช่คำตอบเพราะอยู่นอก Java และอาศัยการเข้ารหัส ASCII และเมื่อรู้ว่าอักขระนั้นเป็นตัวอักษรอยู่แล้ว เป็นการแฮ็กตัวจับเวลาแบบเก่า:c[0] |= ' ';
Mike Dunlavey


นั่นเป็นคำถามที่แตกต่าง
Andy

คำตอบ:


127

ผมทดสอบวิธีการที่มีแนวโน้มการใช้JMH รหัสมาตรฐานแบบเต็มรหัส

สมมติฐานในระหว่างการทดสอบ (เพื่อหลีกเลี่ยงการตรวจสอบกรณีมุมทุกครั้ง): ความยาวสตริงอินพุตจะมากกว่า 1 เสมอ

ผล

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

คะแนนคือการดำเนินการต่อวินาทียิ่งดี

การทดสอบ

  1. test1 เป็นแนวทางแรกของ Andy และ Hllink:

    string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
    
  2. test2เป็นแนวทางที่สองของ Andy นอกจากนี้ยังIntrospector.decapitalize()แนะนำโดย Daniel แต่ไม่มีifคำแถลงสองข้อ อันดับแรกifถูกลบออกเนื่องจากสมมติฐานการทดสอบ อันที่สองถูกลบออกเนื่องจากละเมิดความถูกต้อง (เช่นอินพุต"HI"จะส่งคืน"HI") นี่เกือบจะเร็วที่สุด

    char c[] = string.toCharArray();
    c[0] = Character.toLowerCase(c[0]);
    string = new String(c);
    
  3. test3เป็นการแก้ไขtest2แต่แทนที่จะCharacter.toLowerCase()เป็นฉันเพิ่ม 32 ซึ่งทำงานได้อย่างถูกต้องก็ต่อเมื่อสตริงอยู่ใน ASCII นี่เป็นวิธีที่เร็วที่สุด c[0] |= ' 'จากความคิดเห็นของไมค์ให้ประสิทธิภาพเดียวกัน

    char c[] = string.toCharArray();
    c[0] += 32;
    string = new String(c);
    
  4. test4ใช้StringBuilderแล้ว

    StringBuilder sb = new StringBuilder(string);
    sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
    string = sb.toString();
    
  5. test5ใช้สองsubstring()สาย

    string = string.substring(0, 1).toLowerCase() + string.substring(1);
    
  6. test6ใช้การสะท้อนเพื่อเปลี่ยนchar value[]โดยตรงใน String นี่เป็นสิ่งที่ช้าที่สุด

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

ข้อสรุป

ถ้าความยาวเชือกอยู่เสมอมากกว่า 0 test2การใช้งาน

ถ้าไม่เราต้องตรวจสอบกรณีมุม:

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

ถ้าคุณแน่ใจว่าข้อความของคุณจะเสมอใน ASCII test3และคุณกำลังมองหาประสิทธิภาพมากเพราะคุณพบรหัสนี้ในคอขวดการใช้งาน


95

ฉันเจอทางเลือกที่ดีถ้าคุณไม่ต้องการใช้ไลบรารีของบุคคลที่สาม:

import java.beans.Introspector;

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

14
จากเอกสารสำหรับวิธีนี้: "โดยปกติหมายถึงการแปลงอักขระตัวแรกจากตัวพิมพ์ใหญ่เป็นตัวพิมพ์เล็ก แต่ในกรณีพิเศษ (ผิดปกติ) เมื่อมีอักขระมากกว่าหนึ่งตัวและทั้งอักขระตัวแรกและตัวที่สองเป็นตัวพิมพ์ใหญ่เราจะปล่อยให้ มันคนเดียว”
Andy

1
นอกจากนี้เมื่อดูที่แหล่งที่มาเมื่อวิธีนี้จัดการกับกรณีพิเศษที่ฉันอธิบายไว้ในความคิดเห็นก่อนหน้านี้จะใช้อาร์เรย์ถ่านตามที่ฉันได้กล่าวไว้ในคำถาม
Andy

2
สิ่งที่ฉันต้องการ Introspector.decapitalize ("ABC") จะยังคงเป็น ABC WordUtils.uncapitalize ("ABC") สร้าง "aBC" เพียงแค่แบ่งปันว่าในอดีตเป็นวิธีที่ฤดูใบไม้ผลิอัตโนมัติในการตั้งชื่อถั่วดังนั้นหากคุณต้องการดึงข้อมูลด้วยชื่อถั่ว ABCService ไม่ใช่ aBCService แต่ยังคงเป็น ABCService
ชาวบ้าน

21

เมื่อมันมาถึงการจัดการสตริงจะดูไปจาการ์ตาคอมมอนส์แลงStringUtils


8
โดยเฉพาะอย่างยิ่งเมธอด uncapitalize (java.lang.String) การใช้ StringUtils มีข้อดีเพิ่มเติมคือไม่ต้องกังวลกับ NullPointerExceptions ในโค้ดของคุณ
hexium

3
ไม่จำเป็นต้องมีประสิทธิภาพมากที่สุด แต่อาจจะชัดเจนที่สุดซึ่งมีค่ามาก
David Gelhar

2
ขึ้นอยู่กับทรัพยากรที่คุณกำลังทำให้มีประสิทธิภาพมากขึ้น - เวลาของ CPU หรือโปรแกรมเมอร์ :)
Dan Gravell

15

หากคุณต้องการใช้ Apache Commons คุณสามารถทำสิ่งต่อไปนี้:

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

ผลลัพธ์: someString


3
เป็นทางออกที่ดีและสะอาด แต่ตอนนี้เลิกใช้แล้วเราควรใช้ commons-text:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
dk7

10

แม้จะมีวิธีการเชิงถ่านฉันขอแนะนำโซลูชันที่มุ่งเน้นสตริง String.toLowerCaseเป็นภาษาเฉพาะดังนั้นฉันจะคำนึงถึงปัญหานี้ด้วย String.toLowerCaseคือการต้องการสำหรับล่าง caseing ตามCharacter.toLowerCase นอกจากนี้โซลูชันที่มุ่งเน้นถ่านยังไม่สามารถใช้งานร่วมกับ Unicode แบบเต็มได้เนื่องจากCharacter.toLowerCase ไม่สามารถจัดการกับอักขระเสริมได้

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

อัปเดต: เพื่อเป็นตัวอย่างความสำคัญของการตั้งค่าสถานที่ให้เราใช้ตัวพิมพ์เล็กIในภาษาตุรกีและภาษาเยอรมัน:

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

จะให้ผลลัพธ์ที่แตกต่างกันสองแบบ:

ผม

ผม


7

สตริงใน Java ไม่เปลี่ยนรูปดังนั้นจะสร้างสตริงใหม่ด้วยวิธีใดก็ได้

ตัวอย่างแรกของคุณอาจมีประสิทธิภาพมากกว่าเล็กน้อยเนื่องจากต้องสร้างสตริงใหม่เท่านั้นไม่ใช่อาร์เรย์อักขระชั่วคราว


1
ที่จริงวิธีแรกสร้าง String ชั่วคราว (สำหรับสตริงย่อย) ซึ่งมีราคาแพงกว่าอาร์เรย์อักขระ
Hot Licks

1
ไร้ประโยชน์หากไม่มีข้อมูลสนับสนุน
Nitsan Wakart

3

วิธีการคงที่สั้นและง่ายมากในการเก็บถาวรสิ่งที่คุณต้องการ:

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

2

หากสิ่งที่คุณต้องการนั้นง่ายมาก (เช่นชื่อคลาส java ไม่มีโลแคล) คุณยังสามารถใช้คลาสCaseFormatในไลบรารีGoogle Guava

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

หรือคุณสามารถเตรียมและนำอ็อบเจ็กต์คอนเวอร์เตอร์กลับมาใช้ใหม่ซึ่งอาจมีประสิทธิภาพมากกว่า

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

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

เพื่อทำความเข้าใจปรัชญาของการจัดการสตริง Google ฝรั่ง, ตรวจสอบหน้านี้วิกิพีเดีย



1

ฉันเจอสิ่งนี้เฉพาะวันนี้ พยายามทำด้วยตัวเองที่สุดในทางเดินเท้า ที่ใช้เวลาหนึ่งบรรทัด tho longish นี่ไป

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

ให้:

ก่อน str = TaxoRanks

หลังจาก str = taxoRanks


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.