Многие знакомы с книгой Никлауса Вирта «Алгоритмы и структуры данных». Раньше она называлась «Алгоритмы + структуры данных = программы». И это действительно все, что нам нужно, при этом все-таки данные первичны, так что давайте как следует изучим наши «кирпичики». Ведь специалист должен знать свой инструмент. Сегодня обратимся к базовым типам. Вы скажите «а что тут такого сложного? я не хочу тратить свое время на чтение такой фигни». Хорошо. Тогда ответьте мне на один вопрос: сколько в памяти нужно выделить для переменной типа boolean? Ответ не так прост как может показаться С или С++ программисту, но мы выясним это опытным путем =)
Перед началом нашего удивительного исследования мира двузначной логики и его размеров пройдемся по всем базовым типам:
Примитивный тип | Зарезервированное слово | Размер | Мин. значение | Макс. значение | Атомарный |
---|---|---|---|---|---|
Boolean | boolean | ? | — | — | + |
Character | char | 8 бит = 1 байт | Unicode 0 | Unicode 2^16-1 | + |
Byte integer | byte | 8 бит = 1 байт | -128 | 127 | + |
Short integer | short | 16 бит = 2 байта | -2^15 | 2^15 — 1 | + |
Integer | int | 32 бита = 4 байта | -2^31 | 2^31 — 1 | + |
Long integer | long | 64 бита = 8 байт | -2^63 | 2^63 — 1 | — |
Floating-point | float | 32 бита = 4 байта | IEEE 754 | IEEE 754 | — |
Double precision floating point | double | 64 бита = 8 байт | IEEE 754 | IEEE 754 | — |
Атомарность типа говорит о том, что виртуальная машина выполняет операции над ним «в один заход». Например типы с плавающей запятой не атомарны, так как операции над мантиссой и экспонентой выполняются последовательно. Поэтому при использовании нескольких потоков в приложении, если разные потоки получат доступ к этой переменной, то последствия непредсказуемы.
На этой ноте следует поговорить о модификаторе volatile. Этот модификатор имеет смысл только при многопоточном программировании. Он покажет компилятору, что вы собираетесь использовать эту переменную в нескольких потоках. Для такой переменной важна актуальность значение (если ее значение изменилось в одном потоке, то в другом это изменение должно быть отражено немедленно), что и гарантирует модификатор volatile. На самом деле потоки имеют кешированные копии переменных, поэтому может возникать ситуация с неактуальностью значения, volatile запретит это кеширование.
Таким образом, если переменная будет считываться/модифицироваться более чем в одном потоке — объявляете ее volatile.
Внимание: в отличии от примитивных типов других языков, примитивные типы Java имеют фиксированный размер, что, отчасти, вызвано принципом «Write once, run anywhere».
Так что же у нас с булевским типом? Обратимся к документации. Тут можно найти следующую информацию:
boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its «size» isn’t something that’s precisely defined.
Говорят «не определен точно». Проведем эксперимент. В этом нам поможет нижеследующий код:
public class Runner {
static class BooleanContainer {
boolean a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
boolean b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
boolean c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
boolean d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
boolean e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
static class IntegerContainer {
int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
private static long getMemory() {
Runtime runtime = Runtime.getRuntime();
return runtime.totalMemory() - runtime.freeMemory();
}
private static final int SIZE = 100000;
private static final int NUM_OF_PRIMITIVES = 80;
public static void main(String[] args) throws Exception {
BooleanContainer[] first = new BooleanContainer[SIZE];
IntegerContainer[] second = new IntegerContainer[SIZE];
System.gc();
long startMem = getMemory();
for (int i = 0; i < SIZE; i++) {
first[i] = new BooleanContainer();
}
System.gc();
long endMem = getMemory();
System.out.println("Size for LotsOfBooleans: " + (endMem - startMem));
System.out.println("Average size: "
+ ((endMem - startMem) / ((double) SIZE*NUM_OF_PRIMITIVES)));
System.gc();
startMem = getMemory();
for (int i = 0; i < SIZE; i++) {
second[i] = new IntegerContainer();
}
System.gc();
endMem = getMemory();
System.out.println("Size for LotsOfInts: " + (endMem - startMem));
System.out.println("Average size: "
+ ((endMem - startMem) / ((double) SIZE*NUM_OF_PRIMITIVES)));
/*
* Чтобы избежать сбора наших объектов слишком рано,
* нужно просимулировать действия с этими объектами.
* Ради интереса попробуйте убрать этот код и запустить
* программу.
*/
for (int i = 0; i < SIZE; i++) {
if (first[i].a0) { second[i].a0 += 0;}
}
}
}
Теперь посмотрим, что выводится в консоль:
Size for BooleanContainer: 9244424
Average size: 1.155553
Size for IntegerContainer: 33600000
Average size: 4.2
Замечу, что это результаты на моей машине, то есть JVM для Mac OS X, для других они вероятно будут немного иными.
Выводы напрашиваются сами собой, размер boolean — примерно 1 байт, теперь можно предполагать, что boolean — родственник byte.
Напоследок рассмотрим инициализацию массивов примитивных типов и два полезных метода toString() и deepToString() класса Arrays.
int[] vector = {1,2,3};
int[] vector2 = new int[3];
System.out.println(Arrays.toString(vector));
int[][] matrix = {{1,2},{3,4}};
int[][] matrix2 = new int[2][2];
System.out.println(Arrays.deepToString(matrix));
int[][] jaggedArray = {{1},{1,2,3},{1,2}};
int[][] jaggedArray2 = new int[3][];
jaggedArray2[0] = new int[1];
jaggedArray2[1] = new int[3];
jaggedArray2[2] = new int[2];
System.out.println(Arrays.deepToString(jaggedArray));
Вывод в консоль будет таким:
[1, 2, 3]
[[1, 2], [3, 4]]
[[1], [1, 2, 3], [1, 2]]