- Найти длину массива в Java
- 1. Использование length имущество
- 2. Использование отражения
- 3. Использование Guava
- О размерах в Java
- Типы данных в Java
- Примитивы
- Объекты
- Массивы
- Выравнивание
- И что же получается?
- Что почитать по теме
- Java длина массива байт
- Чтение массива байтов и класс ByteArrayInputStream
- Запись массива байт и класс ByteArrayOutputStream
Найти длину массива в Java
В этом посте мы обсудим, как найти длину массива в Java.
1. Использование length имущество
Стандартное решение для определения длины массива в Java использует его конечную переменную экземпляра. length . Обратите внимание, что длина не является методом. Поэтому он вызывается как .length и не .length() .
2. Использование отражения
The java.lang.reflect.Array Класс предоставляет несколько статических служебных методов для динамического создания массивов Java и доступа к ним. Вы можете использовать его getLength() метод для получения длины указанного массива.
3. Использование Guava
Другой подход состоит в том, чтобы получить список фиксированного размера, поддерживаемый указанным массивом, и вызвать его size() метод.
В библиотеке Google Guava есть Ints.asList() который возвращает список фиксированного размера.
Для массивов объектов вы можете напрямую вызывать метод Arrays.asList() метод. Обратите внимание, что это не будет работать для примитивных массивов, в отличие от Guava. Ints.asList() метод
Это все, что касается нахождения длины массива в Java.
Средний рейтинг 5 /5. Подсчет голосов: 10
Голосов пока нет! Будьте первым, кто оценит этот пост.
Сожалеем, что этот пост не оказался для вас полезным!
Расскажите, как мы можем улучшить этот пост?
Спасибо за чтение.
Пожалуйста, используйте наш онлайн-компилятор размещать код в комментариях, используя C, C++, Java, Python, JavaScript, C#, PHP и многие другие популярные языки программирования.
Как мы? Порекомендуйте нас своим друзьям и помогите нам расти. Удачного кодирования 🙂
Этот веб-сайт использует файлы cookie. Используя этот сайт, вы соглашаетесь с использованием файлов cookie, нашей политикой, условиями авторского права и другими условиями. Читайте наши Политика конфиденциальности. Понятно
О размерах в Java
Задался недавно вопросом: «Как правильно оценить размер выделяемой памяти под объекты в Java?». На хабре есть несколько статей [1], [2] посвященных этому вопросу. Но мне не совсем понравился подход, использованный авторами. Поэтому решил заглянуть внутрь OpenJDK Hotspot VM (далее по тексту Hotspot) и попытаться понять как все устроено на самом деле.
Типы данных в Java
- Примитивы. (byte, short, char, int, float, long, double, boolean).
- Объекты. Размер объекта зависит от конкретной реализации VM и архитектуры процессора. Поэтому дать однозначный ответ не получится. Все же хочется понять (на примере конкретной VM) какой размер памяти выделяется под java-объект.
- Массивы. Одномерные линейные структуры, которые могут содержать все перечисленные типы (включая другие массивы). Массивы также являются объектами, но со специфичной структурой.
Примитивы
С размером примитивов все понятно — их размер определен в спецификации языка (JLS 4.2) и спецификации jvm (JVMS 2.3). Интересно заметить, что для типа boolean jvm использует int, а не byte как могло бы показаться (JMS 2.3.4). Также интересно, что при создании массива boolean[] под каждый элемент массива будет выделен 1 байт, а не 4.
тип | размер (байт) | размер в массиве (байт) | допустимые значения |
---|---|---|---|
byte | 1 | 1 | -128 .. 127 |
short | 2 | 2 | -32768 .. 32767 |
chart | 2 | 2 | ‘\u0000’ .. ‘\uffff’ |
int | 4 | 4 | -2147483648 .. 2147483647 |
float | 4 | 4 | -3.4028235e+38f .. 3.4028235e+38f |
long | 8 | 8 | -9223372036854775808 .. 9223372036854775807 |
double | 8 | 8 | -1.7976931348623157e+308 .. 1.7976931348623157e+308 |
boolean | 4 | 1 | false, true |
Объекты
Для описания экземпляров массивов Hotspot использует класс arrayOopDesc, для описания остальных Java-классов используется класс instanceOopDesc. Оба эти класса наследуются от oopDesc и оба содержат методы для вычисления размера заголовка. Так например a instabceOopDesc вычисляет размер заголовка (в машинных словах) следующим образом:
static int header_size() return sizeof(instanceOopDesc)/HeapWordSize; >
где HeapWordSize определяется как размер указателя. В зависимости от архитектуры CPU 4 и 8 байт для x86 и x86_64 (в Oracle именуют x64) соответственно. Чтобы понять размер instanceOopDesc надо заглянуть в oopDesc, так как в самом instanceOopDesc никаких полей не объявлено. Вот что мы там увидим:
class oopDesc // . volatile markOop _mark; union _metadata wideKlassOop _klass; narrowOop _compressed_klass; > _metadata; // . >;
В файле oopsHierarchy.hpp объявлены необходимые типы данных для работы с иерархией объектов oop (ordinary object pointer). Посмотрим как объявлены те типы, которые используются в oopDesc:
// . typedef juint narrowOop; // Offset instead of address for an oop within a java object typedef class klassOopDesc* wideKlassOop; // to keep SA happy and unhandled // oop detector happy. // . typedef class markOopDesc* markOop; // .
То есть это два указателя (читай два машинных слова) для конкретной архитектуры — так называемое маркировочное слово (mark word) и адрес (который может быть представлен указателем или смещением) на метаданные класса. Идея этого union metadata состоит в том, что при включенной опции -XX:+UseCompressedOops будет использоваться 32х битное смещение (_compressed_klass) а не 64х битный адрес (_klass). Получается размер заголовка java-объекта 8 байт для x86 и 16 байт для x86_64 в не зависимости от параметра UseCompressedOops:
Архитектура | -XX:+UseCompressedOops | -XX:-UseCompressedOops |
---|---|---|
x86 | 8 байт ( 4 + 4 ) | |
x86_64 | 16 байт ( 8 + 8 ) |
Массивы
В arrayOopDesc размер заголовка вычисляется следующим образом:
static int header_size_in_bytes() size_t hs = align_size_up(length_offset_in_bytes() + sizeof(int), HeapWordSize); // . return (int)hs; >
- align_size_up — инлайнер для выравнивания первого аргумента по второму. Например align_size_up(12, 8) = 16 .
- length_offset_in_bytes — возвращает размер заголовка в байтах в зависимости от опции -XX:+UseCompressedOops . Если она включена, то размер равен sizeof(markOop) + sizeof(narrowOop) = 8 (4 + 4) байт для x86 и 12 (8 + 4) байт для x86_64. При выключенной опции размер равен sizeof(arrayOopDesc) = 8 байт для x86 и 16 байт для x86_64.
- заметьте, что к вычисленному размеру прибавляется sizeof(int) . Это делается для того чтобы «зарезервировать место» под поле length массива, так как оно явно не определено в классе. При включенной ссылочной компрессии (актуально только для 64x битной архитектуры) это поле займет вторую половину поля _klass (см. класс oopDesc)
Посчитаем, что у нас получается. Размер заголовка массива после выравнивания:
Архитектура | -XX:+UseCompressedOops | -XX:-UseCompressedOops |
---|---|---|
x86 | 12 байт ( 4 + 4 + 4 align 4 ) | |
x86_64 | 16 байт ( 8 + 4 + 4 align 8 ) | 24 байта ( 8 + 8 + 4 align 8 ) |
Выравнивание
Для предотвращения ситуаций ложного совместного использования строки кэша (cache-line false sharing) размер объекта в Hotspot выравнивается по 8 байтовой границе. То есть если объект будет занимать даже 1 байт под него выделится 8 байт. Размер границы выравнивания выбирается таким образом, чтобы строка кэша была кратна этой границе, а также эта граница должна быть степенью двойки, а также кратна машинному слову. Так как у большинства современных процессоров размер строки кэша составляет 64 байта, а размер машинного слова — 4/8 байт, то размер границы был выбран равным 8 байт. В файле globalDefinitions.hpp есть соответствующие определения (строки 372 — 390). Здесь не буду приводить, интересующиеся могут сходить и посмотреть.
Начиная с версии jdk6u21 размер выравнивания стал настраиваемым параметром. Его можно задать при помощи параметра -XX:ObjectAlignmentInBytes=n . Допустимы значения 8 и 16.
И что же получается?
А получается следующая картина (для x86_64):
public class Point // 0x00 +------------------+ private int x; // | mark word | 8 bytes private int y; // 0x08 +------------------+ private byte color; // | klass oop | 8 bytes // 0x10 +------------------+ public Point(int x, int y, byte color) // | x | 4 bytes this.x = x; // | y | 4 bytes this.y = y; // | color | 1 byte this.color = color; // 0x19 +------------------+ > // | padding | 7 bytes // | | // . // 0x20 +------------------+ > // total: 32 bytes
Для массива char[] из 11 элементов (для x86_64):
char[] str = new char[] // 0x00 +------------------+ 'H', 'e', 'l', 'l', 'o', ' ', // | mark word | 8 bytes 'W', 'o', 'r', 'l', 'd' >; // 0x08 +------------------+ // | klass oop | 4 bytes // 0x0c +------------------+ // | length | 4 bytes // 0x10 +------------------+ // | 'H' | 22 bytes // | 'e' | // | 'l' | // | 'l' | // | 'o' | // | ' ' | // | 'W' | // | 'o' | // | 'r' | // | 'l' | // | 'd' | // 0x26 +------------------+ // | padding | 2 bytes // 0x28 +------------------+ // total: 40 bytes
Что почитать по теме
Java длина массива байт
Для работы с массивами байтов — их чтения и записи используются классы ByteArrayInputStream и ByteArrayOutputStream.
Чтение массива байтов и класс ByteArrayInputStream
Класс ByteArrayInputStream представляет входной поток, использующий в качестве источника данных массив байтов. Он имеет следующие конструкторы:
ByteArrayInputStream(byte[] buf) ByteArrayInputStream(byte[] buf, int offset, int length)
В качестве параметров конструкторы используют массив байтов buf , из которого производится считывание, смещение относительно начала массива offset и количество считываемых символов length .
Считаем массив байтов и выведем его на экран:
import java.io.*; public class Program < public static void main(String[] args) < byte[] array1 = new byte[]; ByteArrayInputStream byteStream1 = new ByteArrayInputStream(array1); int b; while((b=byteStream1.read())!=-1) < System.out.println(b); >String text = "Hello world!"; byte[] array2 = text.getBytes(); // считываем 5 символов ByteArrayInputStream byteStream2 = new ByteArrayInputStream(array2, 0, 5); int c; while((c=byteStream2.read())!=-1) < System.out.println((char)c); >> >
В отличие от других классов потоков для закрытия объекта ByteArrayInputStream не требуется вызывать метод close .
Запись массива байт и класс ByteArrayOutputStream
Класс ByteArrayOutputStream представляет поток вывода, использующий массив байтов в качестве места вывода.
Чтобы создать объект данного класса, мы можем использовать один из его конструкторов:
ByteArrayOutputStream() ByteArrayOutputStream(int size)
Первая версия создает массив для хранения байтов длиной в 32 байта, а вторая версия создает массив длиной size .
Рассмотрим применение класса:
import java.io.*; public class Program < public static void main(String[] args) < ByteArrayOutputStream baos = new ByteArrayOutputStream(); String text = "Hello Wolrd!"; byte[] buffer = text.getBytes(); try< baos.write(buffer); >catch(Exception ex) < System.out.println(ex.getMessage()); >// превращаем массив байтов в строку System.out.println(baos.toString()); // получаем массив байтов и выводим по символьно byte[] array = baos.toByteArray(); for(byte b: array) < System.out.print((char)b); >System.out.println(); > >
Как и в других потоках вывода в классе ByteArrayOutputStream определен метод write , который записывает в поток некоторые данные. В данном случае мы записываем в поток массив байтов. Этот массив байтов записывается в объекте ByteArrayOutputStream в защищенное поле buf , которое представляет также массив байтов ( protected byte[] buf ).
Так как метод write может сгенерировать исключение, то вызов этого метода помещается в блок try..catch.
Используя методы toString() и toByteArray() , можно получить массив байтов buf в виде текста или непосредственно в виде массива байт.
С помощью метода writeTo мы можем вывести массив байт в другой поток. Данный метод в качестве параметра принимает объект OutputStream , в который производится запись массива байт:
ByteArrayOutputStream baos = new ByteArrayOutputStream(); String text = «Hello Wolrd!»; byte[] buffer = text.getBytes(); try < baos.write(buffer); >catch(Exception ex) < System.out.println(ex.getMessage()); >try(FileOutputStream fos = new FileOutputStream(«hello.txt»)) < baos.writeTo(fos); >catch(IOException e)
После выполнения этой программы в папке с программой появится файл hello.txt, который будет содержать строку «Hello Wolrd!».
И в заключении также надо сказать, что как и для объектов ByteArrayInputStream, для ByteArrayOutputStream не надо явным образом закрывать поток с помощью метода close .