JavaRush /Java блог /Random UA /Метод charAt() в Java

Метод charAt() в Java

Стаття з групи Random UA
Є безліч базових методів, які ми регулярно використовуємо, навіть не замислюючись. Ну а що, якщо замислитись та подивитися, яким чином реалізовані деякі прості, на перший погляд, методи? Думаю, це допоможе нам стати на крок ближче до Java) charAt() в Java - 1Уявимо ситуацію, в якій нам потрібно витягнути певний символ у якомусь рядку. Як ми можемо це зробити в Java? Наприклад, за допомогою виклику методу Java String charAt. Про метод charAt()ми поговоримо в сьогоднішній статті.

Синтаксис

char charAt(int index)повертає значення char за вказаним індексом. Індекс коливається від 0 до length()-1. Тобто, перше charзначення послідовності знаходиться в index 0, наступне - index 1і т.д., як і у випадку з індексацією масиву.

приклад

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));
}
У першому рядку береться перший символ, у другому - другий, і так далі. Так як тут використовується не println, а printбез переходу на новий рядок, ми отримаємо висновок в консоль:

Java
Якщо charпід заданим індексом представлений у вигляді юнікод, результатом роботи методу java charAt()буде символ, який представляє даний юнікод:
System.out.println("J\u0061vaRush".charAt(1));
Виведення в консоль:

a

Що "під капотом"

Як воно працює, запитаєте ви? charAt() в Java - 2Справа в тому, що в кожному об'єкті Stringє масив byteз байтами елементів даного рядка:
private final byte[] value;
А ось і сам метод chatAt:
public char charAt(int index) {
   if (isLatin1()) {
       return StringLatin1.charAt(value, index);
   } else {
       return StringUTF16.charAt(value, index);
   }
}
isLatin1— прапор, який вказує на те, чи є в нашому рядку лише латинські символи чи ні. Від цього залежить, який метод буде викликатись далі.

isLatin1 = true

Якщо у рядку є лише латинські символи, викликається статичний метод charAtкласу StringLatin1:
public static char charAt(byte[] value, int index) {
   if (index < 0 || index >= value.length) {
       throw new StringIndexOutOfBoundsException(index);
   }
   return (char)(value[index] & 0xff);
}
Насамперед перевіряється, що індекс, що прийшов більше або дорівнює 0, і що він не виходить за межі внутрішнього масиву байт, і якщо це не так, то кидається виняток - new StringIndexOutOfBoundsException(index). Якщо перевірки пройдені, далі береться потрібний нам елемент. Наприкінці ми бачимо:
  • &розширює для двійкової операції для byteпобітово
  • 0xffнічого не робить, але &вимагає аргументу
  • (char)наводить дані по таблиці ASCII доchar

isLatin1 = false

Якщо ж у нас були присутні не тільки латинські символи, то використовуватиметься клас StringUTF16і викликатиметься його статичний метод:
public static char charAt(byte[] value, int index) {
   checkIndex(index, value);
   return getChar(value, index);
}
Який у свою чергу викликає:
public static void checkIndex(int off, byte[] val) {
   String.checkIndex(off, length(val));
}
А він делегує статичним методом String:
static void checkIndex(int index, int length) {
   if (index < 0 || index >= length) {
       throw new StringIndexOutOfBoundsException("index " + index +
                                                 ", length " + length);
   }
}
Тут, власне, відбувається перевірка на допустимість індексу: знову ж таки, чи він позитивний або нуль, і чи не виходив він за межі масиву. Але в класі StringUTF16в методі charAtцікавішим буде виклик другого методу:
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));
}
Приступимо до розбору, що тут власне відбувається. Насамперед на початку методу йде ще одна перевірка допустимості індексу. Щоб зрозуміти те, що відбувається далі, потрібно усвідомити: якщо не латинський символ потрапляє в масив value, його представляють два байти (два осередки масиву). Якщо ми маємо рядок з двох кирабочних символів — “ав”, то:
  • для 'а' це пара байтів - 48 та 4;
  • для 'в' - 50 та 4.
Тобто, якщо ми створимо рядок "ав", у неї буде масив value- {48, 4, 50, 4} Власне, у цьому методі йде робота з двома осередками масиву value. Тому далі йде зрушення index <<= 1;, щоб потрапити безпосередньо на індекс першого байта шуканого символу в масиві value. А тепер припустимо, що у нас є рядок "абвг". Тоді масив value буде виглядати так: {48, 4, 49, 4, 50, 4, 51, 4}. Ми просимо третій елемент рядка, і тоді двійкове уявлення - 00000000 00000011. При зрушенні на 1 ми отримаємо 00000000 00000110, тобто index = 6. Щоб освіжити знання з побітових операцій, можеш почитати цю статтю . charAt() в Java - 4Також бачимо деякі змінні: HI_BYTE_SHIFT у разі дорівнює 0. LO_BYTE_SHIFTв даному випадку дорівнює 8. В останньому рядку даного методу:
  1. Береться елемент з масиву value і побитово зсувається на HI_BYTE_SHIFT, тобто 0 при цьому збільшуючи index +1.

    У прикладі з рядком "абвг"шостий байт - 51 - так би і залишився, але при цьому збільшується індекс до 7.

  2. Після цього береться наступний елемент масиву і побітово зсувається, але на LO_BYTE_SHIFT , тобто на 8 бітів.

    І якщо у нас це був байт 4, який має двійкову виставу - 00000000 00000100, то після зсуву на 8 бітів у нас буде 00000100 00000000. Якщо цілим числом - 1024.

  3. Далі цих двох значень слід операція | (OR).

    І якщо у нас були байти 51 і 1024, які в двійковому поданні виглядали як 00000000 00110011 і 00000100 00000000, то після операції ми ORотримаємо 00000100 00110011, що означає число 10.

    Ну і врешті-решт перекладається число 1075 у тип char, а при перекладі int -> char використовується таблиця ASCII, і в ній під номером 1075 стоїть символ 'г'.

Власне, таким чином ми отримаємо 'г' як результат роботи методу charAt()в Java-програмуванні.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ