문자열의 첫 번째 문자를 소문자로 만드는 가장 효율적인 방법은 무엇입니까?


97

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

나는 이것을 달성하는 다른 많은 훌륭한 방법이 있다고 확신합니다. 추천 메뉴가 무엇인가요?


가장 좋은 방법은 가능하면 요구 사항을 변경하는 것입니다. String 대신 StringBuilder를 수락하고 직접 수정할 수 있습니다.
Mark Peters

이것은 Java 외부에 있고 ASCII 인코딩과 문자가 이미 알파벳임을 알고 있기 때문에 대답이 아닙니다. 그것은 구시대의 해킹이다 :c[0] |= ' ';
Mike Dunlavey


그 다른 질문
앤디

답변:


123

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] |= ' 'Mike의 의견 에서 동일한 성능을 제공했습니다.

    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
또한 소스를 살펴보면이 메서드가 이전 주석에서 설명한 특수한 경우를 처리하면 내 질문에서 언급 한대로 char 배열 만 사용합니다.
Andy

2
정확히 내가 필요한 것. Introspector.decapitalize ( "ABC")는 여전히 ABC입니다. WordUtils.uncapitalize ( "ABC")는 "aBC"를 생성합니다. 전자를 공유하는 것은 스프링이 빈의 자동 이름 지정을 수행하는 방법이므로 빈 이름으로 ABCService를 검색해야하는 경우 aBCService가 아니라 ABCService입니다.
마을 주민

21

문자열 조작에 관해서는 Jakarta Commons Lang StringUtils를 살펴보십시오 .


8
보다 구체적으로, StringUtils를 사용하는 uncapitalize (java.lang.String) 메소드는 코드에서 NullPointerExceptions에 대해 걱정할 필요가 없다는 추가 이점이 있습니다.
hexium

3
반드시 가장 효율적이지는 않지만 아마도 가장 명확 할 것입니다.
David Gelhar

2
CPU 또는 프로그래머 시간 : - 좀 더 효율적 무엇 자원 따라 달라집니다
댄 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

char 지향 접근 방식에도 불구하고 String 지향 솔루션을 제안합니다. String.toLowerCase 는 로케일에 따라 다르 므로이 문제를 고려해야합니다. Character.toLowerCaseString.toLowerCase 에 따라 소문자를 선호하는 것입니다 . 또한 문자 지향 솔루션은 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
실제로 첫 번째 방법은 문자 배열보다 비용이 많이 드는 임시 문자열 (하위 문자열 용)을 만듭니다.
Hot Licks 2012 년

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 클래스 이름, 로케일 없음) Google Guava 라이브러리 의 CaseFormat 클래스를 사용할 수도 있습니다 .

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 Guava 문자열 조작의 철학을 더 잘 이해하려면 이 위키 페이지를 확인 하세요 .


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

1

나는 오늘만 이것을 건너왔다. 가장 보행자 방식으로 직접 시도했습니다. 그것은 한 줄을 사용했습니다. 여기 간다

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 이후


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

결과:

res4: String = hello
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.