내 프로젝트에서 다음과 같은 SQL 쿼리 빌드를 찾았습니다.
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
이것이 StringBuilder
목표를 달성 합니까 , 즉 메모리 사용량을 줄이는 것입니까?
생성자에서 ‘+'(문자열 연결 연산자)가 사용되기 때문에 의심합니다. 아래 코드와 같이 String을 사용하는 것과 같은 양의 메모리를 사용합니까? s를 사용할 때 다릅니다 StringBuilder.append()
.
return "select id1, " + " id2 " + " from " + " table";
두 문장 모두 메모리 사용량이 같습니까? 명확히하십시오.
미리 감사드립니다!
편집하다:
BTW, 내 코드가 아닙니다 . 오래된 프로젝트에서 찾았습니다. 또한 쿼리는 내 예제의 쿼리만큼 작지 않습니다. 🙂
답변
StringBuilder를 사용하는 목적, 즉 메모리 감소. 달성 되었습니까?
아니, 전혀. 해당 코드가 StringBuilder
올바르게 사용되지 않습니다 . (나는 당신이 그것을 잘못 인용하지만 것 같은데, 확실히이 따옴표없는 id2
및 table
?)
목표는 (일반적으로) 사용 된 총 메모리가 아닌 메모리 변동 을 줄여 가비지 수집기의 삶을 조금 더 쉽게 만드는 것입니다.
아래와 같이 String을 사용하는 것과 동일한 메모리가 필요합니까?
아니요, 인용 한 직선 연결보다 더 많은 메모리 변동을 일으킬 것입니다. (JVM 옵티마이 저가 StringBuilder
코드 의 명시 적 코드가 불필요하다고 판단하고 가능한 경우이를 최적화하지 않는 한 )
해당 코드의 작성자가 StringBuilder
(에 대한 논쟁이 있지만 반대도 있습니다.이 답변의 끝 부분에있는 참고 사항 참조) 올바르게 수행하는 것이 좋습니다 (실제로 id2
and 주위에 따옴표가 없다고 가정합니다 table
).
StringBuilder sb = new StringBuilder(some_appropriate_size);
sb.append("select id1, ");
sb.append(id2);
sb.append(" from ");
sb.append(table);
return sb.toString();
내가 열거 한 것을 참고 some_appropriate_size
에 StringBuilder
우리가 추가 할거야 전체 내용에 대한 충분한 용량로 시작 그래서, 생성자입니다. 하나를 지정하지 않은 경우 사용되는 기본 크기는 16 자 입니다. 일반적으로 너무 작아서 StringBuilder
자신을 더 크게 만들기 위해 재 할당을해야합니다 (IIRC, Sun / Oracle JDK에서는 두 배가됩니다. 공간이 부족할 때 append
마다 특정 ] 을 만족시키기 위해 더 많은 것이 필요하다는 것을 알고 있습니다 .)
Sun / Oracle 컴파일러로 컴파일 한 경우 문자열 연결 이 내부적으로 a 를 사용 한다고 들었을 것StringBuilder
입니다. 이것은 사실이며 StringBuilder
전체 표현에 하나 를 사용 합니다. 그러나 기본 생성자를 사용하므로 대부분의 경우 재 할당을 수행해야합니다. 하지만 읽기가 더 쉽습니다. 이 유의 하지 의 진정한 일련의 연결로. 예를 들어 다음 중 하나를 사용합니다 StringBuilder
.
return "prefix " + variable1 + " middle " + variable2 + " end";
대략 다음과 같이 번역됩니다.
StringBuilder tmp = new StringBuilder(); // Using default 16 character size
tmp.append("prefix ");
tmp.append(variable1);
tmp.append(" middle ");
tmp.append(variable2);
tmp.append(" end");
return tmp.toString();
그래도 괜찮습니다. 기본 생성자와 후속 재 할당이 이상적이지는 않지만 충분히 좋으며 연결이 훨씬 더 읽기 쉽습니다.
그러나 그것은 하나의 표현에만 해당됩니다. 이를 위해 여러 StringBuilder
s가 사용됩니다.
String s;
s = "prefix ";
s += variable1;
s += " middle ";
s += variable2;
s += " end";
return s;
결국 다음과 같이됩니다.
String s;
StringBuilder tmp;
s = "prefix ";
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable1);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" middle ");
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable2);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" end");
s = tmp.toString();
return s;
… 아주 못 생겼습니다.
그러나 극소수의 경우를 제외 하고는 중요하지 않으며 특정 성능 문제를 제외하고는 가독성 (유지 보수성을 향상)을 선호 한다는 점을 기억하는 것이 중요합니다 .
답변
추가하려는 “조각”이 이미 모두있는 경우에는 사용할 필요가 없습니다 StringBuilder
. 사용 StringBuilder
및 샘플 코드에 따라 동일한 호출에서 문자열 연결은 더욱 심각하다.
이것이 더 나을 것입니다.
return "select id1, " + " id2 " + " from " + " table";
이 경우 문자열 연결은 실제로 컴파일 타임에 발생 하므로 훨씬 더 단순한 것과 같습니다.
return "select id1, id2 from table";
사용하는 new StringBuilder().append("select id1, ").append(" id2 ")....toString()
것입니다 사실 을 방해 가 연결이 수행하는 강제하기 때문에,이 경우 성능을 실행 시간 대신에, 컴파일 시간. 죄송합니다.
실제 코드를 포함하여 SQL 쿼리를 작성하는 경우 값을 쿼리에서, 그 다른의 분리 는 오히려 SQL에 비해 매개 변수의 값을 지정하는 매개 변수화 된 쿼리를 사용한다는 것입니다 문제.
나는 오래 전에 내가 쓴 / 에String
StringBuffer
대한 기사StringBuilder
가 있습니다. 원칙 StringBuilder
은 동일한 방식으로 적용됩니다 .
답변
[[여기에 좋은 답변이 있지만 아직 정보가 부족한 것 같습니다. ]]
return (new StringBuilder("select id1, " + " id2 " + " from " + " table"))
.toString();
당신이 지적한 바와 같이, 당신이 제시하는 예는 단순하지만 어쨌든 그것을 분석해 봅시다. 여기서 일어나는 일은 컴파일러가 실제로 모든 상수 +
이기 때문에 여기서 작업을 수행한다는 것 "select id1, " + " id2 " + " from " + " table"
입니다. 그래서 이것은 다음과 같이 변합니다.
return new StringBuilder("select id1, id2 from table").toString();
이 경우 분명히 StringBuilder
. 다음을 수행하는 것이 좋습니다.
// the compiler combines these constant strings
return "select id1, " + " id2 " + " from " + " table";
그러나 필드 또는 기타 비 상수를 추가하는 경우에도 컴파일러는 내부를 사용하므로 StringBuilder
정의 할 필요가 없습니다.
// an internal StringBuilder is used here
return "select id1, " + fieldName + " from " + tableName;
내부적으로 이것은 대략 다음과 같은 코드로 바뀝니다.
StringBuilder sb = new StringBuilder("select id1, ");
sb.append(fieldName).append(" from ").append(tableName);
return sb.toString();
실제로 StringBuilder
직접 사용해야하는 유일한 시간 은 조건부 코드가있을 때입니다. 예를 들어, 다음과 같은 코드는 a에 절실합니다 StringBuilder
.
// 1 StringBuilder used in this line
String query = "select id1, " + fieldName + " from " + tableName;
if (where != null) {
// another StringBuilder used here
query += ' ' + where;
}
+
첫번째 라인의 사용은 하나의 StringBuilder
예입니다. 그런 다음 +=
다른 StringBuilder
인스턴스를 사용 합니다. 다음을 수행하는 것이 더 효율적입니다.
// choose a good starting size to lower chances of reallocation
StringBuilder sb = new StringBuilder(64);
sb.append("select id1, ").append(fieldName).append(" from ").append(tableName);
// conditional code
if (where != null) {
sb.append(' ').append(where);
}
return sb.toString();
a를 사용하는 또 다른 시간 StringBuilder
은 여러 메서드 호출에서 문자열을 만들 때입니다. 그런 다음 StringBuilder
인수 를받는 메서드를 만들 수 있습니다 .
private void addWhere(StringBuilder sb) {
if (where != null) {
sb.append(' ').append(where);
}
}
를 사용하는 경우 동시에 StringBuilder
의 모든 사용을 관찰해야합니다 +
.
sb.append("select " + fieldName);
그러면 +
다른 내부 StringBuilder
가 생성됩니다. 물론 이것은 다음과 같아야합니다.
sb.append("select ").append(fieldName);
마지막으로 @TJrowder가 지적했듯이 항상 StringBuilder
. 이렇게하면 char[]
내부 버퍼의 크기를 늘릴 때 생성되는 개체 수가 절약됩니다 .
답변
문자열 작성기를 사용하는 목적이 적어도 전체 범위는 달성되지 않았다고 추측하는 것이 맞습니다.
그러나 컴파일러가 표현식을 볼 때 "select id1, " + " id2 " + " from " + " table"
실제로 StringBuilder
비하인드 씬을 생성 하고 추가 하는 코드를 방출 하므로 최종 결과는 그렇게 나쁘지 않습니다.
그러나 물론 그 코드를 보는 사람은 그것이 일종의 지연이라고 생각할 수밖에 없습니다.
답변
게시 한 코드에는 StringBuilder를 잘못 사용하고 있으므로 이점이 없습니다. 두 경우 모두 동일한 문자열을 빌드합니다. StringBuilder를 +
사용하면 append
메서드를 사용하여 문자열에 대한 작업을 피할 수 있습니다 . 다음과 같이 사용해야합니다.
return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
Java에서 문자열 유형은 변경 불가능한 문자 시퀀스이므로 두 개의 문자열을 추가하면 VM이 두 피연산자가 연결된 새 문자열 값을 만듭니다.
StringBuilder는 새로운 String 객체를 생성하지 않고 다른 값이나 변수를 연결하는 데 사용할 수있는 가변 문자 시퀀스를 제공하므로 때때로 문자열로 작업하는 것보다 더 효율적일 수 있습니다.
이는 다른 메소드 내에서 매개 변수로 전달 된 char 시퀀스의 내용을 변경하는 등 몇 가지 유용한 기능을 제공합니다. 이는 문자열로는 수행 할 수 없습니다.
private void addWhereClause(StringBuilder sql, String column, String value) {
//WARNING: only as an example, never append directly a value to a SQL String, or you'll be exposed to SQL Injection
sql.append(" where ").append(column).append(" = ").append(value);
}
http://docs.oracle.com/javase/tutorial/java/data/buffers.html 에서 자세한 정보
