StringTokenizer
? Converta String
em a char[]
e itere sobre isso? Algo mais?
StringTokenizer
? Converta String
em a char[]
e itere sobre isso? Algo mais?
Respostas:
Eu uso um loop for para iterar a string e uso charAt()
para que cada caractere a examine. Como a String é implementada com uma matriz, o charAt()
método é uma operação de tempo constante.
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
Isso é o que eu faria. Parece o mais fácil para mim.
No que diz respeito à correção, não acredito que exista aqui. Tudo é baseado no seu estilo pessoal.
String.charAt(int)
é apenas fazer value[index]
. Eu acho que você está confundindo chatAt()
com outra coisa que lhe dá pontos de código.
Duas opções
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
ou
for(char c : s.toCharArray()) {
// process c
}
O primeiro é provavelmente mais rápido, e o segundo é provavelmente mais legível.
Observe que a maioria das outras técnicas descritas aqui se decompõe se você estiver lidando com caracteres fora do BMP (Unicode Basic Multilingual Plane ), ou seja, pontos de código que estão fora do intervalo u0000-uFFFF. Isso só acontecerá raramente, uma vez que os pontos de código fora disso são atribuídos principalmente a idiomas mortos. Mas existem alguns caracteres úteis fora disso, por exemplo, alguns pontos de código usados para notação matemática e outros usados para codificar nomes próprios em chinês.
Nesse caso, seu código será:
String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
int curChar = str.codePointAt(offset);
offset += Character.charCount(curChar);
// do something with curChar
}
O Character.charCount(int)
método requer Java 5+.
Concordo que o StringTokenizer é um exagero aqui. Na verdade, eu tentei as sugestões acima e aproveitei o tempo.
Meu teste foi bastante simples: crie um StringBuilder com cerca de um milhão de caracteres, converta-o em String e percorra cada um deles com charAt () / depois de converter em um array de caracteres / com um CharacterIterator milhares de vezes (é claro, certifique-se de faça algo na string para que o compilador não possa otimizar todo o loop :-)).
O resultado no meu Powerbook de 2,6 GHz (que é um mac :-)) e no JDK 1.5:
Como os resultados são significativamente diferentes, a maneira mais direta também parece ser a mais rápida. Curiosamente, charAt () de um StringBuilder parece ser um pouco mais lento que o de String.
BTW, sugiro não usar o CharacterIterator, pois considero o abuso do caracter '\ uFFFF' como "final da iteração" um truque realmente terrível. Em grandes projetos, sempre existem dois caras que usam o mesmo tipo de hack para dois propósitos diferentes e o código trava muito misteriosamente.
Aqui está um dos testes:
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");
No Java 8 , podemos resolvê-lo da seguinte maneira:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
O método chars () retorna um IntStream
como mencionado no doc :
Retorna um fluxo de int estendendo zero os valores de caracteres dessa sequência. Qualquer caractere mapeado para um ponto de código substituto é passado não interpretado. Se a sequência for alterada enquanto o fluxo estiver sendo lido, o resultado será indefinido.
O método codePoints()
também retorna um IntStream
conforme o documento:
Retorna um fluxo de valores de pontos de código dessa sequência. Quaisquer pares substitutos encontrados na sequência são combinados como se por Character.toCodePoint e o resultado é passado para o fluxo. Quaisquer outras unidades de código, incluindo caracteres BMP comuns, substitutos não emparelhados e unidades de código indefinidas, são estendidos em zero aos valores int que são passados para o fluxo.
Qual a diferença entre char e code point? Como mencionado em neste artigo:
O Unicode 3.1 adicionou caracteres suplementares, elevando o número total de caracteres para mais do que os 216 caracteres que podem ser distinguidos por um único 16 bits
char
. Portanto, umchar
valor não possui mais um mapeamento individual para a unidade semântica fundamental no Unicode. O JDK 5 foi atualizado para suportar o conjunto maior de valores de caracteres. Em vez de alterar a definição dochar
tipo, alguns dos novos caracteres suplementares são representados por um par substituto de doischar
valores. Para reduzir a confusão de nomes, um ponto de código será usado para se referir ao número que representa um caractere Unicode específico, incluindo caracteres adicionais.
Finalmente, por que forEachOrdered
e não forEach
?
O comportamento de forEach
é explicitamente não determinístico, onde, quando ele forEachOrdered
executa uma ação para cada elemento desse fluxo, na ordem de encontro do fluxo, se o fluxo tiver uma ordem de encontro definida. Portanto forEach
, não garante que o pedido seja mantido. Verifique também esta pergunta para mais informações.
Para a diferença entre um caractere, um ponto de código, um glifo e um grafema, verifique esta questão .
Existem algumas aulas dedicadas para isso:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
fornece. Um Java char
contém 16 bits e pode conter caracteres Unicode até U + FFFF, mas o Unicode especifica caracteres até U + 10FFFF. Usar 16 bits para codificar Unicode resulta em uma codificação de caracteres de comprimento variável. A maioria das respostas nesta página assume que a codificação Java é uma codificação de comprimento constante, o que está errado.
Se você possui o Guava no caminho de classe, a seguir é uma alternativa bastante legível. A goiaba ainda tem uma implementação de lista personalizada bastante sensata para esse caso, portanto, isso não deve ser ineficiente.
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
ATUALIZAÇÃO: Como o @Alex observou, o Java 8 também CharSequence#chars
deve ser usado. Até o tipo é IntStream, portanto pode ser mapeado para caracteres como:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
Se você precisar percorrer os pontos de código de um String
(consulte esta resposta ), uma maneira mais curta / mais legível é usar o CharSequence#codePoints
método adicionado no Java 8:
for(int c : string.codePoints().toArray()){
...
}
ou usando o fluxo diretamente em vez de um loop for:
string.codePoints().forEach(c -> ...);
Também existe CharSequence#chars
se você deseja um fluxo de caracteres (embora seja umIntStream
, já que não existe CharStream
).
Eu não usaria StringTokenizer
, pois é uma das classes no JDK que é herdada.
O javadoc diz:
StringTokenizer
é uma classe herdada que é mantida por motivos de compatibilidade, embora seu uso seja desencorajado em novo código. Recomenda-se que qualquer pessoa que procure essa funcionalidade use o método splitString
ou ojava.util.regex
pacote.
Se você precisar de desempenho, deverá testar em seu ambiente. Não há outro jeito.
Aqui exemplo de código:
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);
No Java online , recebo:
1 10349420
2 526130
3 484200
0
No Android x86 API 17, recebo:
1 9122107
2 13486911
3 12700778
0
Consulte Os tutoriais de Java: seqüências de caracteres .
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);
}
}
Coloque o comprimento int len
e use o for
loop.
StringTokenizer é totalmente inadequado para a tarefa de quebrar uma string em seus caracteres individuais. Com String#split()
você, você pode fazer isso facilmente usando uma regex que não corresponde a nada, por exemplo:
String[] theChars = str.split("|");
Mas o StringTokenizer não usa expressões regulares, e não há uma string delimitadora que você possa especificar que corresponda ao nada entre os caracteres. Não é um pouco bonito cortar você pode usar para realizar a mesma coisa: usar a própria string, como a cadeia de delimitador (fazendo com que cada personagem em que um delimitador) e tê-lo retornar os delimitadores:
StringTokenizer st = new StringTokenizer(str, str, true);
No entanto, apenas menciono essas opções com o objetivo de descartá-las. Ambas as técnicas dividem a cadeia original em cadeias de um caractere em vez de primitivas de caracteres, e ambas envolvem uma grande sobrecarga na forma de criação de objetos e manipulação de cadeias. Compare isso com a chamada charAt () em um loop for, que incorre em praticamente nenhuma sobrecarga.
Elaborando sobre esta resposta e esta resposta .
As respostas acima apontam o problema de muitas das soluções aqui que não iteram pelo valor do ponto de código - elas teriam problemas com quaisquer caracteres substitutos . Os documentos em java também descrevem o problema aqui (consulte "Representações de caracteres Unicode"). De qualquer forma, aqui está um código que usa alguns caracteres substitutos reais do conjunto Unicode suplementar e os converte novamente em uma String. Observe que .toChars () retorna uma matriz de caracteres: se você estiver lidando com substitutos, necessariamente terá dois caracteres. Este código deve funcionar para qualquer caractere Unicode.
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
Este código de exemplo irá ajudá-lo!
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);
}
}
Então, tipicamente, existem duas maneiras de percorrer a string em java, que já foi respondida por várias pessoas aqui neste tópico, apenas adicionando minha versão dele. First is using
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
Se o desempenho estiver em risco, recomendarei usar o primeiro em tempo constante; caso contrário, o segundo facilita o seu trabalho, considerando a imutabilidade das classes de strings em java.