Типы char, short, int и long
Целый тип char занимает в памяти 1 байт (8 бит) и позволяет выразить в двоичной системе счисления 2^8 значений=256. Тип char может содержать как положительные, так и отрицательные значения. Диапазон изменения значений составляет от -128 до 127.
uchar
Целый тип uchar также занимает в памяти 1 байт, как и тип char, но в отличие от него, uchar предназначен только для положительных значений. Минимальное значение равно нулю, максимальное значение равно 255. Первая буква u в названии типа uchar является сокращением слова unsigned (беззнаковый).
short
Целый тип short имеет размер 2 байта(16 бит) и, соответственно, позволяет выразить множество значений равное 2 в степени 16: 2^16=65 536. Так как тип short является знаковым и содержит как положительные, так и отрицательные значения, то диапазон значений находится между -32 768 и 32 767.
ushort
Беззнаковым типом short является тип ushort, который также имеет размер 2 байта. Минимальное значение равно 0, максимальное значение 65 535.
int
Целый тип int имеет размер 4 байта (32 бита). Минимальное значение -2 147 483 648, максимальное значение 2 147 483 647.
uint
Беззнаковый целый тип uint занимает в памяти 4 байта и позволяет выражать целочисленные значения от 0 до 4 294 967 295.
long
Целый тип long имеет размер 8 байт (64 бита). Минимальное значение -9 223 372 036 854 775 808, максимальное значение 9 223 372 036 854 775 807.
ulong
Целый тип ulong также занимает 8 байт и позволяет хранить значения от 0 до 18 446 744 073 709 551 615.
char ch= 12 ;
short sh=- 5000 ;
int in= 2445777 ;
Так как беззнаковые целые типы не предназначены для хранения отрицательных значений, то попытка установить отрицательное значение может привести к неожиданным последствиям. Вот такой невинный скрипт приведет к бесконечному циклу:
//— бесконечный цикл
void OnStart ()
<
uchar u_ch;
for ( char ch=-128;ch <128;ch++)
<
u_ch=ch;
Print ( «ch = » ,ch, » u_ch = » ,u_ch);
>
>
Правильно будет так:
//— правильный вариант
void OnStart ()
<
uchar u_ch;
for ( char ch=-128;ch <=127;ch++)
<
u_ch=ch;
Print ( «ch = » ,ch, » u_ch = » ,u_ch);
if (ch==127) break ;
>
>
ch= -128 u_ch= 128
ch= -127 u_ch= 129
ch= -126 u_ch= 130
ch= -125 u_ch= 131
ch= -124 u_ch= 132
ch= -123 u_ch= 133
ch= -122 u_ch= 134
ch= -121 u_ch= 135
ch= -120 u_ch= 136
ch= -119 u_ch= 137
ch= -118 u_ch= 138
ch= -117 u_ch= 139
ch= -116 u_ch= 140
ch= -115 u_ch= 141
ch= -114 u_ch= 142
ch= -113 u_ch= 143
ch= -112 u_ch= 144
ch= -111 u_ch= 145
.
//— отрицательные значения нельзя хранить в беззнаковых типах
uchar u_ch=-120;
ushort u_sh=-5000;
uint u_in=-401280;
Шестнадцатеричные: цифры 0-9, буквы а-f или А-F для значений 10-15; начинаются с 0х или 0Х.
0x0A , 0x12 , 0X12 , 0x2f , 0xA3 , 0Xa3 , 0X7C7
char занимает 1 байт?
Чтобы можно было создавать массивы объектов, и при этом чтобы каждый объект в массиве был выравнен согласно соответствующему значению, память, занимаемая объектами может быть дополнена таким образом, чтобы следующий за данным объект располагался по корректному выравненному адресу.
Это позволяет корректно использовать арифметику указателей, так как увеличение указателя на единицу увеличивает его значение на sizeof( тип объекта ) .
В вашем случае объекты класса C выравниваются на границу, соответствующему типу int , и поэтому в конец объекта добавляется 3 байта, чтобы получить кратное sizeof( int ) ..
Если ваш компилятор поддерживает ключевое слово alignof , то вы можете получить значение, по которому выравнивается адрес памяти для объектов ваших классов. Так предложение
std::cout
выводит на консоль значение
которое соответствует значению sizeof( int ) . То есть объекта класса C размещаются на по границе целых чисел. Чтобы это обеспечить, каждый объект дополняется 3 байтами.
Java/Типы данных
В Java есть 8 примитивных типов, которые делят на 4 группы, вот они:
- Целые числа - byte, short, int, long
- Числа с плавающей точкой (иначе вещественные) - float, double
- Логический - boolean
- Символьный - char
Целочисленные типы [ править ]
Целочисленные типы различаются между собой только диапазонами возможных значений, например, для хранения номера элемента в таблице Менделеева пока хватит переменной типа byte.
| Тип | Размер (бит) | Диапазон |
|---|---|---|
| byte | 8 бит | от -128 до 127 |
| short | 16 бит | от -32768 до 32767 |
| char | 16 бит | беззнаковое целое число, представляющее собой символ UTF-16 (буквы и цифры) |
| int | 32 бит | от -2147483648 до 2147483647 |
| long | 64 бит | от -9223372036854775808L до 9223372036854775807L |
Пример использования целочисленных типов:
public class IntegralTypes public static void main(String[] args) byte b = 216; // Вот тут будет ошибка, т.к. у нас диапазон от -128 до 127! short s = 1123; int i = 64536; long l = 2147483648L; // Постфикс l или L обозначает литералы типа long System.out.println(i); System.out.println(b); System.out.println(s); System.out.println(l); > >
Символы тоже относят к целочисленным типам из-за особенностей представления в памяти и традиций.
public class Characters public static void main(String[] args) char a = 'a', b, c = 'c'; b = (char) ((a + c) / 2); // Можно складывать, вычитать, делить и умножать // Но из-за особенностей арифметики Java результат приходится приводить к типу char явно System.out.println(b); // Выведет символ 'b' > >
Типы с плавающей точкой [ править ]
| Тип | Размер (бит) | Диапазон |
|---|---|---|
| float | 32 | от 1.4e-45f до 3.4e+38f |
| double | 64 | от 4.9e-324 до 1.7e+308 |
public class FloatingPointTypes public static void main(String[] args) double a, b = 4.12; a = 22.1 + b; float pi = 3.14f; // При использовании типа float требуется указывать суффикс f или F // так как без них типом литерала будет считаться double float anotherPi = (float) 3.14; // Можно привести явно double c = 27; double d = pi * c; System.out.println(d); > >
Логический тип [ править ]
| Тип | Размер (бит) | Значение |
|---|---|---|
| boolean | 8 (в массивах), 32 (не в массивах используется int) | true (истина) или false (ложь) |
В стандартной реализации Sun JVM и Oracle HotSpot JVM тип boolean занимает 4 байта (32 бита), как и тип int. Однако, в определенных версиях JVM имеются реализации, где в массиве boolean каждое значение занимает по 1-му байту.
Ссылочные [ править ]
Ссылочные типы - это все остальные типы: классы, перечисления и интерфейсы, например, объявленные в стандартной библиотеке Java, а также массивы.
Строки [ править ]
Строки это объекты класса String, они очень распространены, поэтому в некоторых случаях обрабатываются отлично от всех остальных объектов. Строковые литералы записываются в двойных кавычках.
public class Strings public static void main(String[] args) String a = "Hello", b = "World"; System.out.println(a + " " + b); // Здесь + означает объединение (конкатенацию) строк // Пробел не вставляется автоматически // Строки конкатенируются слева направо, надо помнить это когда соединяешь строку и примитив String c = 2 + 2 + ""; // "4" String d = "" + 2 + 2; // "22" d = "" + (2 + 2); // а теперь d тоже "4" String foo = "a string"; String bar = "a string"; // bar будет указывать на тот же объект что и foo String baz = new String("a string"); // Чтобы гарантированно создать новую строку надо вызвать конструктор System.out.println("foo == bar ? " + (foo == bar)); // == сравнивает ссылки на объекты System.out.println("foo равен bar ? " + (foo.equals(bar))); // Метод equals служит для проверки двух объектов на равенство System.out.println("foo == baz ? " + (foo == baz)); System.out.println("foo равен baz ? " + (foo.equals(baz))); > >
Эта программа выведет:
Hello World
foo == bar ? true
foo равен bar ? true
foo == baz ? false
foo равен baz ? true
Обертки [ править ]
Если требуется создать ссылку на один из примитивных типов данных, необходимо использовать соответствующий класс-обертку. Также в таких классах есть некоторые полезные методы и константы, например минимальное значение типа int можно узнать использовав константу Integer.MIN_VALUE. Оборачивание примитива в объект называется упаковкой (boxing), а обратный процесс распаковкой (unboxing).
| Тип | Класс-обертка |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| char | Character |
| float | Float |
| double | Double |
| boolean | Boolean |
int i; Integer boxed; // Обычное создание объекта boxed = new Integer(i); // Фабричный метод boxed = Integer.valueOf(i); // Автоматическая упаковка, компилятор просто вставит вызов Integer.valueOf boxed = i;
Рекомендуется использовать valueOf, он может быть быстрее и использовать меньше памяти потому что применяет кэширование, а конструктор всегда создает новый объект.
Получить примитив из объекта-обертки можно методом Value.
Integer boxed; int i; // Явная распаковка i = boxed.intValue(); // Автоматическая распаковка i = boxed;
Что быстрее: char(1), integer или smallint?
Комментарий от 17.10.2003. Вопрос остался актуальным, несмотря на появление в Borland InterBase 7.x типа BOOLEAN. Этот тип данных имеет длину 4 байта, и все равно в большинстве случаев требует специальной обработки TField.GetText/SetText, если визуализация значения столбца отличается от checkbox или True/False.
Очень часто возникает необходимость хранения однобайтовых значений – 0/1, Y/N, М/Ж и т. п. Многие задаются вопросом – какой тип поля использовать в данном случае? Char(1) или smallint? А может, integer? На первый взгляд, кажется что char(1) лучше. Но если вспомнить, как IB хранит символьные данные, то окажется, что char(1) на самом деле занимает 3 байта (2 байта длины и 1 байт на символ). По этой же причине varchar(1) вообще не рассматривается, поскольку занимает вообще 5 байт (2 байта длины, 1 байт на символ, 2 байта на количество концевых пробелов). Integer занимает 4 байта, smallint соответственно 2.
Если исходить из размера типа данных, то кажется, что лучше выбрать smallint. Однако, вы увидите из теста, что это не совсем верное предположение.
Аппаратура и программное обеспечение
- MB ASUS P5NP6, Pentium Pro 150MHz 128K cache, 128MB RAM, HDD Quantum Fireball ST 6.4Gb (IDE), BusMaster drivers.
- Windows NT Workstation 4.0, SP4, RollUp post sp4 fix.
- IB Database 5.5.0.742
DATABASE_CACHE_PAGES 7500
SERVER_CLIENT_MAPPING 8192
SERVER_WORKING_SIZE_MIN 10000
SERVER_WORKING_SIZE_MAX 70000
LOCK_HASH_SLOTS 511
База данных TEST.GDB создана с размером страницы 8192 байта. Все тестовые запросы выполнялись через локальный интерфейс ("e:\test.gdb").
Тестовые таблицы
Основная таблица для тестов по скорости выборки и поиска:
CREATE TABLE TT (
ID INTEGER NOT NULL PRIMARY KEY,
C1 CHAR(1),
I INTEGER,
S SMALLINT)
Таблицы для определения размера хранимых данных:
CREATE TABLE C1 (C1 CHAR(1));
CREATE TABLE I (I INTEGER);
CREATE TABLE S (S SMALLINT);
Процедура заливки
Предварительно создан генератор idgen и объявлена функция GetRandom и SetRandSeed из randomudf. GetRandom(10) выдает случайное число от 0 до 9.
create procedure filltt (rnum integer)
as
declare variable i integer;
declare variable r integer;
begin
i=1;
while (:i < :rnum) do
begin
r=getrandom(10);
insert into tt values(gen_id(idgen, 1), cast(:r as char(1)), :r, :r);
i=:i+1;
end
end
Перед вызовом процедуры инициализирован генератор случайных чисел – SELECT SETRANDSEED(0) FROM RDB$DATABASE – чтобы Вы могли повторить этот тест (получить идентичное распределение random).
Примечание. Если вы решили повторить этот тест, не забудьте перед наполнением таблицы и созданием индексов выключить Forced Writes – это сэкономит обращение к диску при записи данных.
Вызовом EXECUTE PROCEDURE FILLTT(1000000) создан 1 миллион записей (можно и порциями, например, по 100 тысяч – не имеет значения). Созданы 3 индекса по всем полям (отдельно) для каждого поля:
CREATE INDEX BYC1 ON TT (C1);
CREATE INDEX BYI ON TT (I);
CREATE INDEX BYS ON TT (S);
Статистика
Для чистоты эксперимента база данных установлена в режим "no reserve" (не резервировать пространство на страницах для версий записей) при помощи IB_WISQL, и сделан backup/restore. На самом деле, изначально я проводил тест на БД, у которой заполнение страниц таблиц было ~52%, т. е. при отключенном параметре "no reserve". Оказалось, что на скорость выполнения запросов это не влияет никоим образом, причем время выполнения запросов идентично вплоть до сотых секунды в обоих случаях. Параметр "no reserve" был установлен только для того, чтобы максимально точно определить разницу в объеме хранимых данных для каждого типа индексов (и таблиц C1, I, S статистика приведена в другом разделе этого документа. См. дальше). По крайней мере предполагалось, что это поможет. На самом деле получается, что "no reserve" ни на что, кроме объема базы данных, не влияет.
Database header page information:
Flags 0
Checksum 12345
Generation 13
Page size 8192
ODS version 9.1
Oldest transaction 1
Oldest active 2
Oldest snapshot 1
Next transaction 6
Bumped transaction 1
Sequence number 0
Next attachment ID 0
Implementation ID 16
Shadow count 0
Page buffers 0
Next header page 0
Creation date Jul 22, 1999 17:12:53
Attributes no reserve
Variable header data:
Sweep interval: 0
*END*
Database file sequence:
File e:\test.gdb is the only file
Database log page information:
Creation date
Log flags: 2
No write ahead log
Next log page: 0
Variable log data:
Control Point 1:
File name:
Partition offset: 0 Seqno: 0 Offset: 0
Control Point 2:
File name:
Partition offset: 0 Seqno: 0 Offset: 0
Current File:
File name:
Partition offset: 0 Seqno: 0 Offset: 0
*END*
Analyzing database pages .
TT (128)
Primary pointer page: 133, Index root page: 134
Data pages: 4362, data page slots: 4362, average fill: 91%
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 0
60 - 79% = 0
80 - 99% = 4362
Index BYC1 (1)
Depth: 2, leaf buckets: 737, nodes: 1000000
Average data length: 0.00, total dup: 999990, max dup: 32707
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 0
60 - 79% = 0
80 - 99% = 737
Index BYI (2)
Depth: 2, leaf buckets: 737, nodes: 1000000
Average data length: 0.00, total dup: 999990, max dup: 32707
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 0
60 - 79% = 0
80 - 99% = 737
Index BYS (3)
Depth: 2, leaf buckets: 737, nodes: 1000000
Average data length: 0.00, total dup: 999990, max dup: 32707
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 0
60 - 79% = 0
80 - 99% = 737
Index RDB$PRIMARY1 (0)
Depth: 2, leaf buckets: 860, nodes: 1000000
Average data length: 1.00, total dup: 0, max dup: 0
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 0
60 - 79% = 0
80 - 99% = 860
Как ясно видно из статистики, индексы по полям имеют абсолютно одинаковое количество страниц. С одной стороны это означает, что с точки зрения индекса нет разницы между char(1), integer и smallint, а с другой – что индексы не будут вносить искажения в результаты запросов.
Запросы
Параметр IBCONFIG – DATABASE_CACHE_PAGES был выбран таким (7500), чтобы постараться вместить в кэш как все страницы таблицы, так и страницы используемого индекса. Например, количество страниц таблицы – 4362, одного индекса – 737. Итого 4362+737 = 5099. Это минимум для database_cache_pages.
Поиск количества по одному значению – выборка диапазона из индекса, и просчет по нему count. (Несущественные параметры статистики убраны. Одинаковые значения статистики для разных запросов оставлены только у первого запроса)
SELECT COUNT(C1) FROM TT
WHERE C1 = 0
PLAN (TT INDEX (BYC1))
Elapsed time= 5.52 sec
Buffers = 7500
Reads = 0
Fetches = 199843
SELECT COUNT(C1) FROM TT
WHERE C1 = '0'
PLAN (TT INDEX (BYC1))
Elapsed time= 4.36 sec
SELECT COUNT(I) FROM TT
WHERE I = 0
PLAN (TT INDEX (BYI))
Elapsed time= 3.24 sec
SELECT COUNT(S) FROM TT
WHERE S = 0
PLAN (TT INDEX (BYS))
Elapsed time= 3.53 sec
Группировка всех значений в памяти. Параметр Reads показывает, что кэша в памяти не хватает для всех страниц таблицы и индекса, и происходит считывание с диска. Однако для всех запросов это значение одинаково, поэтому факт чтения страниц с диска можно игнорировать.
SELECT C1, COUNT(C1) FROM TT
GROUP BY C1
ORDER BY C1
PLAN (TT ORDER BYC1)
0 99878
1 99468
2 100207
3 99988
4 99988
5 100127
6 100265
7 100196
8 100156
9 99727
Elapsed time= 71.42 sec
Buffers = 7500
Reads = 743
Fetches = 3000750
SELECT I, COUNT(I) FROM TT
GROUP BY I
ORDER BY I
PLAN (TT ORDER BYI)
0 99878
1 99468
2 100207
3 99988
4 99988
5 100127
6 100265
7 100196
8 100156
9 99727
Elapsed time= 59.80 sec
SELECT S, COUNT(S) FROM TT
GROUP BY S
ORDER BY S
PLAN (TT ORDER BYS)
0 99878
1 99468
2 100207
3 99988
4 99988
5 100127
6 100265
7 100196
8 100156
9 99727
Elapsed time= 59.99 sec
Битовое слияние индексов при поиске (выборке диапазона значений):
SELECT COUNT(C1) FROM TT
WHERE C1 = 0 OR C1 = 1
PLAN (TT INDEX (BYC1,BYC1))
Elapsed time= 13.67 sec
SELECT COUNT(C1) FROM TT
WHERE C1 = '0' OR C1 = '1'
Elapsed time= 10.22 sec
SELECT COUNT(I) FROM TT
WHERE I = 0 OR I = 1
PLAN (TT INDEX (BYI,BYI))
Elapsed time= 7.25 sec
SELECT COUNT(S) FROM TT
WHERE S = 0 OR S = 1
PLAN (TT INDEX (BYS,BYS))
Elapsed time= 8.04 sec
Fetches = 398865
И последний тест – на чистый объем занимаемых данных. Посчитать в таблице TT это невозможно, поэтому я создал три отдельные таблицы, состоящие только из соответствующего поля, и перенес данные из tt в эти таблицы (insert into x select x from tt).
Примечание. Максимальный размер БД после всех тестов достигает 230 мегабайт (с параметром no_reserve – 144 мегабайта), а backup – до 90 мегабайт. Следовательно, для повторения теста потребуется минимум 320 мегабайт дискового пространства.
C1 (129)
Primary pointer page: 135, Index root page: 136
Data pages: 3437, data page slots: 3437, average fill: 93%
Fill distribution:
0 - 19% = 0
20 - 39% = 1
40 - 59% = 0
60 - 79% = 0
80 - 99% = 3436
I (130)
Primary pointer page: 137, Index root page: 138
Data pages: 3437, data page slots: 3437, average fill: 93%
Fill distribution:
0 - 19% = 0
20 - 39% = 1
40 - 59% = 0
60 - 79% = 0
80 - 99% = 3436
S (131)
Primary pointer page: 139, Index root page: 140
Data pages: 3437, data page slots: 3437, average fill: 93%
Fill distribution:
0 - 19% = 0
20 - 39% = 1
40 - 59% = 0
60 - 79% = 0
80 - 99% = 3436
Из статистики следует, что разница в объеме данных в миллион записей между char(1), integer и smallint отсутствует. Таким образом, даже предположение, что smallint будет занимать на диске места меньше чем integer, неверно (не говоря о char(1)).
Выводы
- Если вы храните числа в строках, никогда не производите сравнение значения поля с числом. Обрамляйте число кавычками, т. е. производите сравнение значения поля со строкой.
- Разницы в объеме занимаемых данных между char(1), integer и smallint нет как для таблиц, так и для индексов.
- Скорость обработки integer и smallint выше чем char(1), и integer обрабатывается быстрее smallint.
- Для хранения булевских значений integer подходит намного лучше char(1). Можно воспользоваться и smallint.
C1 (128)
Primary pointer page: 144, Index root page: 145
Data pages: 6897, data page slots: 6897, average fill: 93%
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 1
60 - 79% = 0
80 - 99% = 6896
I (129)
Primary pointer page: 146, Index root page: 147
Data pages: 6897, data page slots: 6897, average fill: 93%
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 1
60 - 79% = 0
80 - 99% = 6896
S (130)
Primary pointer page: 149, Index root page: 150
Data pages: 6897, data page slots: 6897, average fill: 93%
Fill distribution:
0 - 19% = 0
20 - 39% = 0
40 - 59% = 1
60 - 79% = 0
80 - 99% = 6896
Copyright iBase.ru © 2002-2023
