StringTokenizer
? แปลงString
ไปเป็น a char[]
และทำซ้ำมากกว่านั้นหรือไม่ อื่น ๆ อีก?
StringTokenizer
? แปลงString
ไปเป็น a char[]
และทำซ้ำมากกว่านั้นหรือไม่ อื่น ๆ อีก?
คำตอบ:
ฉันใช้สำหรับวนรอบเพื่อย้ำสตริงและใช้charAt()
เพื่อให้ตัวละครแต่ละตัวตรวจสอบมัน เนื่องจาก String ถูกนำมาใช้กับอาร์เรย์charAt()
วิธีการคือการดำเนินการเวลาคงที่
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
นั่นคือสิ่งที่ฉันจะทำ ดูเหมือนง่ายที่สุดสำหรับฉัน
เท่าที่ถูกต้องไปฉันไม่เชื่อว่ามีอยู่ที่นี่ มันขึ้นอยู่กับสไตล์ส่วนตัวของคุณ
String.charAt(int)
value[index]
ฉันคิดว่าคุณสับสนchatAt()
กับสิ่งอื่นที่ให้คะแนนรหัสแก่คุณ
สองตัวเลือก
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
หรือ
for(char c : s.toCharArray()) {
// process c
}
ครั้งแรกน่าจะเร็วกว่าแล้วที่สองน่าจะอ่านได้มากกว่า
สังเกตเทคนิคอื่น ๆ ที่อธิบายไว้ที่นี่ส่วนใหญ่หากคุณจัดการกับตัวละครนอก BMP (Unicode Basic Multilingual Plane ) นั่นคือจุดรหัสที่อยู่นอกช่วง u0000-uFFFF สิ่งนี้จะเกิดขึ้นได้ไม่บ่อยนักเนื่องจากรหัสจุดนอกนี้ส่วนใหญ่จะถูกกำหนดให้กับภาษาที่ตาย แต่มีอักขระที่มีประโยชน์อยู่ด้านนอกตัวอย่างเช่นบางจุดรหัสที่ใช้สำหรับสัญกรณ์คณิตศาสตร์และบางตัวใช้เพื่อเข้ารหัสชื่อที่เหมาะสมในภาษาจีน
ในกรณีนั้นรหัสของคุณจะเป็น:
String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
int curChar = str.codePointAt(offset);
offset += Character.charCount(curChar);
// do something with curChar
}
Character.charCount(int)
วิธีการต้องใช้ Java 5+
ฉันยอมรับว่า StringTokenizer เกินความเป็นจริงที่นี่ ที่จริงฉันลองทำตามคำแนะนำด้านบนแล้วใช้เวลา
การทดสอบของฉันค่อนข้างง่าย: สร้าง StringBuilder ที่มีประมาณหนึ่งล้านตัวอักษรแปลงเป็นสตริงและสำรวจแต่ละอักขระด้วย charAt () / หลังจากแปลงเป็นอาร์เรย์ถ่าน / ด้วย CharacterIterator พันครั้ง (แน่นอนว่าทำให้แน่ใจว่า ทำบางสิ่งบางอย่างบนสตริงเพื่อให้คอมไพเลอร์ไม่สามารถปรับห่วงทั้งหมด :-))
ผลลัพธ์ใน Powerbook 2.6 GHz ของฉัน (นั่นคือ mac :-)) และ JDK 1.5:
เนื่องจากผลลัพธ์มีความแตกต่างอย่างมีนัยสำคัญวิธีที่ตรงไปตรงมาที่สุดก็ดูเหมือนจะเป็นวิธีที่เร็วที่สุด น่าสนใจ charAt () ของ StringBuilder ดูเหมือนจะช้ากว่าหนึ่งใน String เล็กน้อย
BTW ฉันแนะนำไม่ให้ใช้ CharacterIterator เพราะฉันพิจารณาว่าการใช้อักขระ '\ uFFFF' ในทางที่ผิดเป็นการ "สิ้นสุดการทำซ้ำ" แฮ็คที่น่ากลัวจริงๆ ในโครงการขนาดใหญ่มักมีผู้ชายสองคนที่ใช้การแฮ็กประเภทเดียวกันเพื่อวัตถุประสงค์ที่แตกต่างกันสองรายการและรหัสขัดข้องอย่างลึกลับ
นี่คือหนึ่งในการทดสอบ:
int count = 1000;
...
System.out.println("Test 1: charAt + String");
long t = System.currentTimeMillis();
int sum=0;
for (int i=0; i<count; i++) {
int len = str.length();
for (int j=0; j<len; j++) {
if (str.charAt(j) == 'b')
sum = sum + 1;
}
}
t = System.currentTimeMillis()-t;
System.out.println("result: "+ sum + " after " + t + "msec");
ในJava 8เราสามารถแก้ปัญหาได้ดังนี้:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
chars ของ method () จะคืนค่า a IntStream
ตามที่กล่าวไว้ในdoc :
ส่งคืนสตรีมของ int ที่ไม่มีการขยายค่าถ่านจากลำดับนี้ อักขระใด ๆ ที่แม็พกับจุดโค้ดตัวแทนจะถูกส่งผ่านโดยไม่ถูกตีความ หากมีการเปลี่ยนแปลงลำดับในขณะที่กระแสกำลังอ่านผลลัพธ์จะไม่ได้กำหนด
วิธีcodePoints()
นี้ยังส่งคืนIntStream
เอกสารตาม:
ส่งคืนกระแสของค่ารหัสจุดจากลำดับนี้ คู่ตัวแทนที่พบในลำดับจะถูกรวมกันเสมือนว่าโดย Character.toCodePoint และผลลัพธ์จะถูกส่งผ่านไปยังสตรีม หน่วยโค้ดอื่นใดรวมถึงตัวอักษร BMP ธรรมดาตัวแทนตัวแทนที่ไม่ได้รับการคู่และหน่วยรหัสที่ไม่ได้กำหนดจะถูกขยายเป็นศูนย์ถึงค่า int ซึ่งจะถูกส่งผ่านไปยังสตรีม
ถ่านและจุดรหัสแตกต่างกันอย่างไร เป็นที่กล่าวถึงในนี้บทความ:
Unicode 3.1 เพิ่มอักขระเสริมซึ่งทำให้จำนวนอักขระรวมเกิน 216 ตัวที่สามารถแยกความแตกต่างด้วย 16 บิต
char
เดียว ดังนั้นchar
ค่าจึงไม่มีการแมปแบบหนึ่งต่อหนึ่งกับหน่วยความหมายพื้นฐานใน Unicode อีกต่อไป ปรับปรุง JDK 5 เพื่อรองรับชุดอักขระที่มีขนาดใหญ่ขึ้น แทนที่จะเปลี่ยนคำจำกัดความของchar
ประเภทอักขระเสริมใหม่บางตัวจะถูกแทนด้วยคู่ตัวแทนสองchar
ค่า เพื่อลดความสับสนในการตั้งชื่อจุดรหัสจะถูกใช้เพื่ออ้างถึงหมายเลขที่แสดงถึงอักขระ Unicode เฉพาะรวมถึงตัวเสริม
ในที่สุดทำไมforEachOrdered
และไม่forEach
?
พฤติกรรมของforEach
คือ nondeterministic อย่างชัดเจนโดยที่การforEachOrdered
ดำเนินการสำหรับแต่ละองค์ประกอบของสตรีมนี้ในลำดับการเผชิญหน้าของสตรีมหากสตรีมมีลำดับการเผชิญหน้าที่กำหนดไว้ ดังนั้นforEach
ไม่รับประกันว่าคำสั่งจะถูกเก็บไว้ ตรวจสอบคำถามนี้เพิ่มเติม
สำหรับความแตกต่างระหว่างตัวละคร, จุดโค้ด, glyph และกราฟตรวจสอบคำถามนี้
มีคลาสเฉพาะสำหรับเรื่องนี้:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
จัดเตรียมไว้ Java char
มี 16 บิตและสามารถเก็บอักขระ Unicode ได้สูงสุด U + FFFF แต่ Unicode จะระบุอักขระได้สูงสุด U + 10FFFF การใช้ 16 บิตเพื่อเข้ารหัส Unicode ส่งผลให้มีการเข้ารหัสอักขระความยาวผันแปร คำตอบส่วนใหญ่ในหน้านี้ถือว่าการเข้ารหัส Java เป็นการเข้ารหัสความยาวคงที่ซึ่งไม่ถูกต้อง
หากคุณมีGuavaอยู่ใน classpath ของคุณสิ่งต่อไปนี้เป็นทางเลือกที่อ่านง่าย Guava ยังมีการใช้งานรายการที่กำหนดเองที่สมเหตุสมผลสำหรับกรณีนี้ดังนั้นจึงไม่มีประสิทธิภาพ
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
UPDATE: ตามที่ @Alex ระบุไว้ด้วย Java 8 ก็มีCharSequence#chars
ให้ใช้เช่นกัน แม้กระทั่งประเภทคือ IntStream ดังนั้นจึงสามารถแมปกับตัวอักษรเช่น:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
หากคุณต้องการวนซ้ำคะแนนรหัสของString
(ดูคำตอบนี้) วิธีที่สั้นลง / อ่านง่ายมากขึ้นคือการใช้CharSequence#codePoints
วิธีการที่เพิ่มใน Java 8:
for(int c : string.codePoints().toArray()){
...
}
หรือใช้สตรีมโดยตรงแทน for loop:
string.codePoints().forEach(c -> ...);
นอกจากนี้ยังมีCharSequence#chars
ถ้าคุณต้องการกระแสของตัวละคร (แม้ว่าจะเป็นIntStream
เพราะไม่มีCharStream
)
ฉันจะไม่ใช้StringTokenizer
เนื่องจากเป็นหนึ่งในคลาสใน JDK ที่เป็นมรดก
javadoc พูดว่า:
StringTokenizer
เป็นคลาสดั้งเดิมที่ถูกเก็บไว้เพื่อเหตุผลด้านความเข้ากันได้แม้ว่าการใช้งานจะไม่ได้รับการสนับสนุนในรหัสใหม่ ขอแนะนำให้ทุกคนที่แสวงหาฟังก์ชั่นนี้ใช้วิธีการแยกของString
หรือjava.util.regex
แพคเกจแทน
หากคุณต้องการประสิทธิภาพคุณต้องทดสอบกับสภาพแวดล้อมของคุณ ไม่มีทางอื่น.
นี่คือตัวอย่างรหัส:
int tmp = 0;
String s = new String(new byte[64*1024]);
{
long st = System.nanoTime();
for(int i = 0, n = s.length(); i < n; i++) {
tmp += s.charAt(i);
}
st = System.nanoTime() - st;
System.out.println("1 " + st);
}
{
long st = System.nanoTime();
char[] ch = s.toCharArray();
for(int i = 0, n = ch.length; i < n; i++) {
tmp += ch[i];
}
st = System.nanoTime() - st;
System.out.println("2 " + st);
}
{
long st = System.nanoTime();
for(char c : s.toCharArray()) {
tmp += c;
}
st = System.nanoTime() - st;
System.out.println("3 " + st);
}
System.out.println("" + tmp);
บนJava ออนไลน์ฉันจะได้รับ:
1 10349420
2 526130
3 484200
0
ใน Android x86 API 17 ฉันได้รับ:
1 9122107
2 13486911
3 12700778
0
ดูจาวา Tutorials: Strings
public class StringDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
char[] tempCharArray = new char[len];
char[] charArray = new char[len];
// put original string in an array of chars
for (int i = 0; i < len; i++) {
tempCharArray[i] = palindrome.charAt(i);
}
// reverse array of chars
for (int j = 0; j < len; j++) {
charArray[j] = tempCharArray[len - 1 - j];
}
String reversePalindrome = new String(charArray);
System.out.println(reversePalindrome);
}
}
ใส่ความยาวเข้าไปint len
และใช้for
ลูป
StringTokenizer ไม่เหมาะที่จะแบ่งสตริงออกเป็นอักขระแต่ละตัวโดยสิ้นเชิง ด้วยการString#split()
ที่คุณสามารถทำได้อย่างง่ายดายโดยใช้ regex ที่ตรงกับอะไรเช่น:
String[] theChars = str.split("|");
แต่ StringTokenizer ไม่ได้ใช้ regexes และไม่มีสตริงตัวคั่นที่คุณสามารถระบุได้ว่าจะตรงกับสิ่งใดระหว่างอักขระ มีเป็นหนึ่งในน้อยน่ารักตัดคุณสามารถใช้เพื่อให้บรรลุในสิ่งเดียวกัน: ใช้สตริงตัวเองเป็นตัวคั่นสตริง (ทำให้ตัวละครทุกตัวในนั้นคั่น) และมีมันกลับคั่น:
StringTokenizer st = new StringTokenizer(str, str, true);
อย่างไรก็ตามฉันพูดถึงตัวเลือกเหล่านี้เพื่อจุดประสงค์ในการยกเลิกเท่านั้น เทคนิคทั้งสองแตกสตริงดั้งเดิมเป็นสตริงอักขระหนึ่งตัวแทนที่จะใช้อักขระพื้นฐานและทั้งสองเกี่ยวข้องกับค่าใช้จ่ายจำนวนมากในรูปแบบของการสร้างวัตถุและการจัดการสตริง เปรียบเทียบกับการเรียก charAt () ใน for for loop ซึ่งไม่มีค่าใช้จ่ายใด ๆ เลย
อธิบายอย่างละเอียดเกี่ยวกับคำตอบนี้และคำตอบนี้
เหนือคำตอบที่ชี้ให้เห็นปัญหาของหลายโซลูชั่นที่นี่ซึ่งไม่สำทับด้วยค่าจุดรหัส - พวกเขาจะมีปัญหาใด ๆ กับตัวอักษรตัวแทน เอกสาร java ยังสรุปปัญหาที่นี่ด้วย (ดู "การแสดงอักขระ Unicode") อย่างไรก็ตามนี่คือรหัสบางส่วนที่ใช้ตัวอักษรตัวแทนบางตัวจากชุด Unicode เสริมและแปลงกลับเป็นสตริง โปรดทราบว่า. toChars () ส่งคืนอาร์เรย์ของตัวอักษร: หากคุณกำลังจัดการกับตัวแทนเสมือนคุณจะต้องมีตัวอักษรสองตัว รหัสนี้ควรใช้ได้กับอักขระ Unicode ใด ๆ
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
รหัสตัวอย่างนี้จะช่วยคุณออกไป!
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Solution {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 10);
map.put("b", 30);
map.put("c", 50);
map.put("d", 40);
map.put("e", 20);
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
}
public static Map sortByValue(Map unsortedMap) {
Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}
class ValueComparator implements Comparator {
Map map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Object keyA, Object keyB) {
Comparable valueA = (Comparable) map.get(keyA);
Comparable valueB = (Comparable) map.get(keyB);
return valueB.compareTo(valueA);
}
}
ดังนั้นโดยทั่วไปมีสองวิธีในการวนซ้ำผ่านสตริงใน java ซึ่งได้รับการตอบแล้วโดยคนหลายคนที่นี่ในหัวข้อนี้เพียงแค่เพิ่มรุ่นของฉันมันก่อนใช้
String s = sc.next() // assuming scanner class is defined above
for(int i=0; i<s.length; i++){
s.charAt(i) // This being the first way and is a constant time operation will hardly add any overhead
}
char[] str = new char[10];
str = s.toCharArray() // this is another way of doing so and it takes O(n) amount of time for copying contents from your string class to character array
หากประสิทธิภาพการทำงานมีความเสี่ยงแล้วฉันจะแนะนำให้ใช้ครั้งแรกในเวลาคงที่ถ้าไม่ไปกับคนที่สองทำให้งานของคุณง่ายขึ้นพิจารณาความไม่เปลี่ยนแปลงกับคลาสสตริงใน java