StringTokenizer
? Dönüştürmek String
a char[]
ve bunun üzerinde yineleme? Başka bir şey?
StringTokenizer
? Dönüştürmek String
a char[]
ve bunun üzerinde yineleme? Başka bir şey?
Yanıtlar:
Dizeyi yinelemek için for döngüsü kullanıyorum charAt()
ve her karakteri incelemek için kullanıyorum. Dize bir dizi ile uygulandığından, charAt()
yöntem sabit bir zaman işlemidir.
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
Ben de öyle yapardım. Benim için en kolay görünüyor.
Doğrulukla ilgili olarak, bunun var olduğuna inanmıyorum. Her şey kişisel tarzınıza dayanmaktadır.
String.charAt(int)
sadece yapıyor value[index]
. Sanırım size chatAt()
kod puanı veren başka bir şeyle karıştırıyorsunuz .
İki seçenek
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
veya
for(char c : s.toCharArray()) {
// process c
}
Birincisi muhtemelen daha hızlı, ikincisi muhtemelen daha okunabilir.
BMP (Unicode Temel Çok Dilli Düzlem ) dışındaki karakterlerle , yani u0000-uFFFF aralığının dışındaki kod noktalarıyla ilgileniyorsanız, burada açıklanan diğer tekniklerin çoğunun parçalandığını unutmayın. Bunun dışındaki kod noktaları çoğunlukla ölü dillere atandığından, bu yalnızca nadiren olur. Ancak bunun dışında bazı yararlı karakterler vardır, örneğin matematiksel gösterim için kullanılan bazı kod noktaları ve bazıları Çince'de uygun adları kodlamak için kullanılır.
Bu durumda kodunuz:
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)
Yöntem Java 5+ gerektirir.
StringTokenizer'in burada aşırı dolu olduğunu kabul ediyorum. Aslında yukarıdaki önerileri denedim ve zaman aldım.
Testim oldukça basitti: yaklaşık bir milyon karakterden oluşan bir StringBuilder oluşturun, bir String'e dönüştürün ve charAt () ile / bir char dizisine / CharacterIterator ile bin kez dönüştürdükten sonra her birini çaprazlayın (tabii ki dize üzerinde bir şey yapın, böylece derleyici tüm döngüyü optimize edemez :-)).
2.6 GHz Powerbook'um (bu bir mac :-)) ve JDK 1.5'teki sonuç:
Sonuçlar önemli ölçüde farklı olduğundan, en basit yol da en hızlı yol gibi görünmektedir. İlginç bir şekilde, bir StringBuilder'ın charAt () yöntemi, String'inkinden biraz daha yavaş görünüyor.
BTW Ben '\ uFFFF' karakterini kötüye kullanımı "yineleme sonu" olarak gerçekten korkunç bir kesmek olarak kabul CharacterIterator kullanmanızı öneririz. Büyük projelerde her zaman iki farklı amaç için aynı tür kesmek kullanan iki adam vardır ve kod gerçekten gizemli bir şekilde çöküyor.
İşte testlerden biri:
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");
In Java 8 bunu olarak çözebilir:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
Yöntem karakter () bir döner IntStream
belirtildiği gibi doküman :
Bu diziden char değerlerini sıfır genişleten bir int akışı döndürür. Bir vekil kod noktasına eşlenen tüm karakterler yorumlanmadan geçirilir. Akış okunurken dizi mutasyona uğratılırsa, sonuç tanımsız olur.
Yöntem codePoints()
ayrıca IntStream
doc başına bir döndürür :
Bu diziden bir kod noktası değerleri akışı döndürür. Dizide karşılaşılan yedek çiftler, Character.toCodePoint tarafından birleştirilir ve sonuç akışa geçirilir. Sıradan BMP karakterleri, eşleştirilmemiş vekiller ve tanımlanmamış kod birimleri de dahil olmak üzere diğer kod birimleri, daha sonra akışa iletilen int değerlerine sıfır olarak genişletilir.
Karakter ve kod noktası arasındaki fark nedir? Bu makalede belirtildiği gibi :
Unicode 3.1 ek karakter ekleyerek toplam karakter sayısını 216 karakterden fazlasını tek bir 16 bit ile ayırt edebilecek şekilde getirdi
char
. Bu nedenle, birchar
değerin artık Unicode'daki temel semantik birim ile bire bir eşlemesi yoktur. JDK 5, daha büyük karakter değerleri kümesini destekleyecek şekilde güncellendi. Türün tanımını değiştirmek yerinechar
, yeni tamamlayıcı karakterlerin bazıları ikichar
değerden oluşan bir vekil çiftle temsil edilir . Adlandırma karışıklığını azaltmak için, ek olanlar da dahil olmak üzere belirli bir Unicode karakterini temsil eden sayıyı belirtmek için bir kod noktası kullanılacaktır.
Sonunda neden forEachOrdered
olmasın forEach
?
Öğesinin davranışı forEach
açıkça belirsizdir; burada forEachOrdered
akış, tanımlı bir karşılaşma sırasına sahipse akışın karşılaşma sırasında bu akışın her öğesi için bir eylem gerçekleştirir . Bu yüzden forEach
siparişin korunacağını garanti etmez. Ayrıca daha fazla bilgi için bu soruyu kontrol edin .
Bir karakter, bir kod noktası, bir glif ve bir grafik arasındaki fark için bu soruyu kontrol edin .
Bunun için bazı özel sınıflar var:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
. Bir Java char
16 bit içerir ve Unicode karakterleri U + FFFF kadar tutabilir ancak Unicode U + 10FFFF değerine kadar olan karakterleri belirtir. Unicode'u kodlamak için 16 bit kullanmak değişken uzunluklu bir karakter kodlamasıyla sonuçlanır. Bu sayfadaki cevapların çoğu, Java kodlamasının yanlış uzunluktaki sabit bir kodlama olduğunu varsayar.
Sınıf yolunuzda Guava varsa , aşağıdakiler oldukça okunabilir bir alternatiftir. Guava'nın bu durum için oldukça makul bir özel Liste uygulaması bile var, bu yüzden bu verimsiz olmamalıdır.
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
GÜNCELLEME: @Alex'in belirttiği gibi, Java 8 ile de CharSequence#chars
kullanılacak. Tür bile IntStream olduğundan, aşağıdaki gibi karakterlerle eşleştirilebilir:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
Bir a'nın kod noktalarını yinelemeniz gerekiyorsa String
(bu cevaba bakınız ) daha kısa / daha okunabilir bir yol CharSequence#codePoints
Java 8'de eklenen yöntemi kullanmaktır :
for(int c : string.codePoints().toArray()){
...
}
veya akışı for for döngüsü yerine doğrudan kullanma:
string.codePoints().forEach(c -> ...);
Ayrıca CharSequence#chars
karakterlerin bir akışını istiyorsanız (o olmasına rağmen IntStream
, çünkü bir yok CharStream
).
StringTokenizer
JDK'daki eski sınıflardan biri olduğu için kullanmazdım .
Javadoc diyor ki:
StringTokenizer
kullanımı yeni kodda kullanılmamasına rağmen uyumluluk nedeniyle saklanan eski bir sınıftır. Bu işlevselliği arayan herkesin bunun yerine split yönteminiString
veyajava.util.regex
paketini kullanması önerilir .
Performansa ihtiyacınız varsa , ortamınızı test etmeniz gerekir . Başka yol yok.
İşte örnek kod:
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);
On Java çevrimiçi alıyorum:
1 10349420
2 526130
3 484200
0
Android x86 API 17'de şunu elde ederim:
1 9122107
2 13486911
3 12700778
0
Bkz . Java Öğreticileri: Dizeler .
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);
}
}
Uzunluğu koyun int len
ve for
döngüyü kullanın .
StringTokenizer, bir dizeyi kendi karakterlerine bölme görevi için tamamen uygun değildir. Bununla String#split()
hiçbir şeyle eşleşen bir normal ifade kullanarak bunu kolayca yapabilirsiniz, örneğin:
String[] theChars = str.split("|");
Ancak StringTokenizer normal ifadeleri kullanmaz ve karakterler arasında hiçbir şeyle eşleşmeyecek belirtebileceğiniz sınırlayıcı bir dize yoktur. Orada ise bu ayraçları dönmek Ayırıcı dize olarak dize kendisi kullanmak (buna her karakteri bir ayırıcı yapma) ve vardır: biri sevimli küçük aynı şeyi gerçekleştirmek için kullanabileceğiniz kesmek:
StringTokenizer st = new StringTokenizer(str, str, true);
Ancak, bu seçeneklerden sadece onları reddetmek amacıyla bahsediyorum. Her iki teknik de orijinal dizgiyi karakter ilkelleri yerine tek karakterli dizelere böler ve her ikisi de nesne oluşturma ve dizgi manipülasyonu şeklinde büyük miktarda ek yük içerir. Bunu neredeyse hiç ek yükü olmayan bir for döngüsünde charAt () öğesini çağırarak karşılaştırın.
Bu cevap ve bu cevap üzerinde duruluyor .
Yukarıdaki yanıtlar, kod noktası değerine göre yinelenmeyen birçok çözümün sorununa işaret ediyor - herhangi bir vekil grafikle sorun yaşayacaklardı . Java dokümanları da sorunu burada özetliyor (bkz. "Unicode Karakter Gösterimleri"). Her neyse, ek Unicode kümesinden bazı gerçek vekil karakterleri kullanan ve bunları tekrar bir String'e dönüştüren bazı kodlar . .ToChars () işlevinin bir dizi karakter döndürdüğünü unutmayın: Taşıyıcılarla uğraşıyorsanız, mutlaka iki karaktere sahip olursunuz. Bu kod herhangi bir Unicode karakteri için çalışmalıdır .
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
Bu Örnek Kod size yardımcı olacaktır!
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);
}
}
Yani tipik olarak, bu konudaki birden fazla kişi tarafından zaten cevaplanmış olan java dizesini yinelemenin iki yolu var, sadece benim versiyonumu ekliyoruz.
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
Performans söz konusu ise, o zaman ilkini sabit zamanda kullanmanızı tavsiye ederim, eğer ikincisi ile devam etmiyorsa, Java'daki string sınıflarıyla değişmezliği göz önünde bulundurarak işinizi kolaylaştırır.