Existem muitas técnicas básicas que usamos regularmente, mesmo sem pensar nisso. Bem, e se você pensar sobre isso e observar como alguns métodos aparentemente simples são implementados? Acho que isso nos ajudará a chegar um passo mais perto do Java. Vamos imaginar uma situação em que precisamos extrair um determinado caractere de alguma string. Como podemos fazer isso em Java? Por exemplo, chamando o
Java String charAt
. charAt()
Falaremos sobre o método no artigo de hoje.
Sintaxe
char charAt(int index)
retorna o valor char no índice especificado. O índice varia de 0 a length()-1
. Ou seja, o primeiro char
valor da sequência está em index 0
, o próximo está em , index 1
etc., como é o caso da indexação de array.
Exemplo
public static void main(String[] args) {
System.out.print("JavaRush".charAt(0));
System.out.print("JavaRush".charAt(1));
System.out.print("JavaRush".charAt(2));
System.out.print("JavaRush".charAt(3));
}
A primeira linha leva o primeiro caractere, a segunda linha leva o segundo e assim por diante. Como not println
, mas é usado aqui print
, sem uma nova linha, obteremos a seguinte saída no console:
Java
Se char
o índice fornecido for representado como Unicode, o resultado do método java charAt()
será o caractere que representa este Unicode:
System.out.println("J\u0061vaRush".charAt(1));
Saída do console:
a
O que está "sob o capô"
Como isso funciona, você pergunta? O fato é que cada objetoString
contém um array byte
com bytes dos elementos de uma determinada linha:
private final byte[] value;
E aqui está o método em si chatAt
:
public char charAt(int index) {
if (isLatin1()) {
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
isLatin1
- um sinalizador que indica se nossa string contém apenas caracteres latinos ou não. Isso determina qual método será chamado a seguir.
isLatin1 = verdadeiro
Se a string contiver apenas caracteres latinos, um métodocharAt
de classe estático será chamado StringLatin1
:
public static char charAt(byte[] value, int index) {
if (index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
}
return (char)(value[index] & 0xff);
}
O primeiro passo é verificar se o índice de entrada é maior ou igual a 0, e se não ultrapassa o array de bytes interno, e caso não seja o caso, então é lançada uma exceção new StringIndexOutOfBoundsException(index)
. Se as verificações forem aprovadas, o elemento que precisamos será obtido. No final vemos:
&
estende a operação binária parabyte
bit a bit0xff
não faz nada, mas&
requer um argumento(char)
converte dados de uma tabela ASCII parachar
isLatin1 = falso
Se não tivéssemos apenas caracteres latinos, a classe será utilizadaStringUTF16
e seu método estático será chamado:
public static char charAt(byte[] value, int index) {
checkIndex(index, value);
return getChar(value, index);
}
Que por sua vez chama:
public static void checkIndex(int off, byte[] val) {
String.checkIndex(off, length(val));
}
E ele delega para um método estático String
:
static void checkIndex(int index, int length) {
if (index < 0 || index >= length) {
throw new StringIndexOutOfBoundsException("index " + index +
", length " + length);
}
}
Aqui, de fato, é feita uma verificação para ver se o índice é válido: novamente, se é positivo ou zero, e se não ultrapassou os limites do array. Mas em uma classe StringUTF16
de método, charAt
chamar o segundo método será mais interessante:
static char getChar(byte[] val, int index) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
Vamos começar a analisar o que realmente está acontecendo aqui. A primeira etapa no início do método é outra verificação da validade do índice. Para entender o que acontece a seguir, é preciso entender: quando um caractere não latino entra no array value
, ele é representado por dois bytes (duas células do array). Se tivermos uma sequência de dois caracteres cirílicos - “av”, então:
- para 'a' é um par de bytes - 48 e 4;
- para 'in' - 50 e 4.
value
- {48, 4, 50, 4} Na verdade, este método funciona com duas células do array value
. Portanto, o próximo passo é um deslocamento index <<= 1;
para chegar diretamente ao índice do primeiro byte do caractere desejado no array value
. Agora digamos que temos uma string "абвг"
. Então a matriz de valores ficará assim: {48, 4, 49, 4, 50, 4, 51, 4}. Pedimos o terceiro elemento da string e então a representação binária é 00000000 00000011. Quando deslocado por 1, obtemos 00000000 00000110, ou seja index = 6
. Para atualizar seu conhecimento sobre operações bit a bit, você pode ler este artigo . Vemos também algumas variáveis: HI_BYTE_SHIFT
neste caso é 0. LO_BYTE_SHIFT
neste caso é 8. Na última linha deste método:
- Um elemento é retirado da matriz de valores e deslocado bit a bit por
HI_BYTE_SHIFT
, ou seja, 0, enquanto aumentaindex +1
.No exemplo com a string
"абвг"
, o sexto byte – 51 – permaneceria assim, mas ao mesmo tempo o índice aumenta para 7. - Depois disso, o próximo elemento do array é obtido e deslocado bit a bit da mesma forma, mas por LO_BYTE_SHIFT, ou seja, por 8 bits.
E se tivéssemos o byte 4, que tem uma representação binária - 00000000 00000100, depois de deslocar 8 bits teremos 00000100 00000000. Se for um número inteiro - 1024.
- A seguir, para estes dois valores, segue a operação
| (OR)
.E se tivéssemos os bytes 51 e 1024, que em representação binária se pareciam com 00000000 00110011 e 00000100 00000000, depois da operação
OR
obteríamos 00000100 00110011, que significa o número 1075 no sistema decimal.Bom, no final, o número 1075 é convertido para o tipo char, e ao converter int -> char, é utilizada a tabela ASCII, e nela, sob o número 1075, está o caractere 'g'.
charAt()
na programação Java.
GO TO FULL VERSION