Cách hiệu quả nhất để tạo ký tự đầu tiên của chuỗi viết thường?


97

Cách hiệu quả nhất để tạo ký tự đầu tiên của Stringchữ thường là gì?

Tôi có thể nghĩ ra một số cách để làm điều này:

Sử dụng charAt()vớisubstring()

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

Hoặc sử dụng một charmảng

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

Tôi chắc chắn rằng có nhiều cách tuyệt vời khác để đạt được điều này. Bạn đề xuất món gì?


Cách tốt nhất là thay đổi yêu cầu của bạn nếu có thể. Chấp nhận một StringBuilder thay vì một Chuỗi và bạn có thể sửa đổi nó trực tiếp.
Mark Peters

Đây không phải là câu trả lời vì nó nằm ngoài Java và dựa vào mã hóa ASCII và biết rằng ký tự đã là bảng chữ cái. Đó là một tuổi-timer của hack:c[0] |= ' ';
Mike Dunlavey


đó là một câu hỏi khác
Andy

Câu trả lời:


123

Tôi đã thử nghiệm các cách tiếp cận đầy hứa hẹn bằng cách sử dụng JMH . điểm chuẩn đầy đủ .

Giả định trong quá trình kiểm tra (để tránh kiểm tra các trường hợp góc mỗi lần): độ dài chuỗi đầu vào luôn lớn hơn 1.

Các kết quả

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

Điểm số là hoạt động trên giây, càng nhiều càng tốt.

Kiểm tra

  1. test1 đầu tiên là cách tiếp cận của Andy và Hllink:

    string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
  2. test2là cách tiếp cận thứ hai của Andy. Nó cũng được Introspector.decapitalize()gợi ý bởi Daniel, nhưng không có hai iftuyên bố. Đầu tiên ifđã bị loại bỏ vì giả định thử nghiệm. Cái thứ hai đã bị xóa vì nó vi phạm tính đúng đắn (tức là đầu vào "HI"sẽ trả về "HI"). Đây gần như là nhanh nhất.

    char c[] = string.toCharArray();
    c[0] = Character.toLowerCase(c[0]);
    string = new String(c);
  3. test3là một sửa đổi của test2, nhưng thay vì Character.toLowerCase(), tôi đã thêm 32, hoạt động chính xác nếu và chỉ khi chuỗi nằm trong ASCII. Đây là nhanh nhất. c[0] |= ' 'từ bình luận của Mike đã cho hiệu suất tương tự.

    char c[] = string.toCharArray();
    c[0] += 32;
    string = new String(c);
  4. test4đã qua sử dụng StringBuilder.

    StringBuilder sb = new StringBuilder(string);
    sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
    string = sb.toString();
  5. test5đã sử dụng hai substring()cuộc gọi.

    string = string.substring(0, 1).toLowerCase() + string.substring(1);
  6. test6sử dụng phản chiếu để thay đổi char value[]trực tiếp trong Chuỗi. Đây là chậm nhất.

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

Kết luận

Nếu độ dài Chuỗi luôn lớn hơn 0, hãy sử dụng test2.

Nếu không, chúng tôi phải kiểm tra các trường hợp góc:

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

Nếu bạn chắc chắn rằng văn bản của bạn sẽ luôn ở trong ASCII và bạn đang tìm kiếm hiệu suất cực cao vì bạn đã tìm thấy mã này trong nút cổ chai, hãy sử dụng test3.


95

Tôi đã tìm thấy một giải pháp thay thế tuyệt vời nếu bạn không muốn sử dụng thư viện của bên thứ ba:

import java.beans.Introspector;

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

14
Từ tài liệu cho phương thức này: "Điều này thường có nghĩa là chuyển đổi ký tự đầu tiên từ chữ hoa thành chữ thường, nhưng trong trường hợp đặc biệt (bất thường) khi có nhiều hơn một ký tự và cả ký tự đầu tiên và ký tự thứ hai đều là chữ hoa, chúng tôi bỏ đi nó một mình. "
Andy

1
Ngoài ra, nhìn vào nguồn, một khi phương thức này xử lý trường hợp đặc biệt mà tôi đã mô tả trong nhận xét trước, nó chỉ sử dụng mảng char như tôi đã đề cập trong câu hỏi của mình.
Andy

2
Chính xác những gì tôi cần. Intros Inspector.decapitalize ("ABC") sẽ vẫn là ABC. WordUtils.uncapitalize ("ABC") tạo ra "aBC". Chỉ chia sẻ rằng đầu tiên là cách Spring thực hiện tự động đặt tên của bean, vì vậy nếu bạn cần lấy ABCService bằng tên bean, thì đó không phải là aBCService mà vẫn là ABCService.
dân làng

21

Khi nói đến thao tác chuỗi, hãy xem Jakarta Commons Lang StringUtils .


8
Cụ thể hơn, phương pháp uncapitalize (java.lang.String) Sử dụng StringUtils có thêm lợi thế là không phải lo lắng về NullPointerExceptions trong mã của bạn.
hexium

3
Không nhất thiết là hiệu quả nhất, nhưng có lẽ là rõ ràng nhất, có giá trị rất nhiều.
David Gelhar

2
Phụ thuộc những gì bạn đang làm cho tài nguyên hiệu quả hơn - CPU hoặc lập trình thời gian :)
Dan Gravell

15

Nếu bạn muốn sử dụng Apache Commons, bạn có thể làm như sau:

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

Kết quả: someString


3
Nó đẹp và giải pháp sạch, nhưng điều này bị phản đối bây giờ, chúng ta nên sử dụng commons-text:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
dk7

10

Mặc dù có một cách tiếp cận định hướng char, tôi sẽ đề xuất một giải pháp định hướng chuỗi. String.toLowerCasengôn ngữ cụ thể, vì vậy tôi sẽ tính đến vấn đề này. String.toLowerCaselà ưu tiên cho cách viết thường theo Character.toLowerCase . Ngoài ra, một giải pháp hướng char không tương thích với unicode đầy đủ, vì Character.toLowerCase không thể xử lý các ký tự bổ sung.

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

CẬP NHẬT: Như một ví dụ về tầm quan trọng của cài đặt ngôn ngữ, hãy cho chúng tôi viết thường Itrong tiếng Thổ Nhĩ Kỳ và tiếng Đức:

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

sẽ xuất ra hai kết quả khác nhau:

Tôi

Tôi


7

Các chuỗi trong Java là bất biến, vì vậy một trong hai cách một chuỗi mới sẽ được tạo.

Ví dụ đầu tiên của bạn có thể sẽ hiệu quả hơn một chút vì nó chỉ cần tạo một chuỗi mới chứ không phải một mảng ký tự tạm thời.


1
Trên thực tế, cách đầu tiên tạo một Chuỗi tạm thời (cho chuỗi con), đắt hơn mảng ký tự.
Hot Licks

1
Không hữu ích khi không có dữ liệu hỗ trợ
Nitsan Wakart

3

Một phương thức tĩnh rất ngắn và đơn giản để lưu trữ những gì bạn muốn:

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

2

Nếu những gì bạn cần rất đơn giản (ví dụ: tên lớp java, không có ngôn ngữ), bạn cũng có thể sử dụng lớp CaseFormat trong thư viện Google Guava .

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

Hoặc bạn có thể chuẩn bị và sử dụng lại đối tượng chuyển đổi, điều này có thể hiệu quả hơn.

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

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

Để hiểu rõ hơn về triết lý của thao tác xâu chuỗi Google Guava, hãy xem trang wiki này .


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

1

Tôi đã đi qua điều này chỉ ngày hôm nay. Cố gắng tự mình làm theo cách của người đi bộ nhất. Đó là một dòng, tho longish. Đây rồi

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

Cung cấp:

Trước str = TaxoRanks

Sau str = taxoRanks


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

Kết quả:

res4: String = hello
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.