Битовые коллекции
32C# --- Руководство по C# --- Битовые коллекции
Если требуется иметь дело с множеством битов, можно применить класс BitArray и структуру BitVector32. Класс BitArray расположен в пространстве имен System.Collections, a BitVector32 — в пространстве System.Collections.Specialized. Наиболее важное отличие между этими двумя типами состоит в том, что BitArray имеет изменяемый размер, а это удобно, когда необходимое количество бит известно заранее, и оно велико. Структура BitVector32 основана на стеке, и потому работает быстрее. BitVector32 содержит только 32 бита, которые хранятся в целом числе.
Класс BitArray
Класс BitArray служит для хранения отдельных битов в коллекции. А поскольку в коллекции этого класса хранятся биты, а не объекты, то своими возможностями он отличается от классов других коллекций. Тем не менее в классе BitArray реализуются интерфейсы ICollection и IEnumerable как основополагающие элементы поддержки всех типов коллекций. Кроме того, в классе BitArray реализуется интерфейс ICloneable.
В классе BitArray определено несколько конструкторов. Так, с помощью приведенного ниже конструктора можно сконструировать объект типа BitArray из массива логических значений:
public BitArray(bool[] values)
В данном случае каждый элемент массива values становится отдельным битом в коллекции. Это означает, что каждому элементу массива values соответствует отдельный бит в коллекции. Более того, порядок расположения элементов в массиве values сохраняется и в коллекции соответствующих им битов. Коллекцию типа BitArray можно также составить из массива байтов, используя следующий конструктор:
public BitArray(byte[] bytes)
Здесь битами в коллекции становится уже целый их набор из массива bytes, причем элемент bytes [0] обозначает первые 8 битов, элемент bytes [1] — вторые 8 битов и т.д.
Коллекции типа BitArray подлежат индексированию. По каждому индексу указывается отдельный бит в коллекции, причем нулевой индекс обозначает младший бит.
В классе BitArray определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Методы этого класса приведены ниже. Обратите внимание на то, что в классе BitArray не поддерживается метод Synchronized(). Это означает, что для коллекций данного класса синхронизированная оболочка недоступна, а свойство IsSynchronized всегда имеет логическое значение false. Тем не менее для управления доступом к коллекции типа BitArray ее можно синхронизировать для объекта, предоставляемого упоминавшимся ранее свойством SyncRoot.
- And()
Выполняет операцию логического умножения (И) битов вызывающего объекта и коллекции value. Возвращает коллекцию типа BitArray, содержащую результат
- Get()
Возвращает значение бита, указываемого по индексу
- Not()
Выполняет операцию поразрядного логического отрицания (НЕ) битов вызывающей коллекции и возвращает коллекцию типа BitArray, содержащую результат
- Or()
Выполняет операцию логического сложения (ИЛИ) битов вызывающего объекта и коллекции value. Возвращает коллекцию типа BitArray, содержащую результат
- Set()
Устанавливает бит, указываемый по индексу index, равным значению value
- SetAll()
Устанавливает все биты равными значению value
- Xor()
Выполняет логическую операцию исключающее (ИЛИ) над битами вызывающего объекта и коллекции value. Возвращает коллекцию типа BitArray, содержащую результат
В классе BitArray определяется также собственное свойство, помимо тех, что указаны в интерфейсах, которые в нем реализуются:
public int Length { get; set; }
Свойство Length позволяет установить или получить количество битов в коллекции. Следовательно, оно возвращает такое же значение, как и стандартное свойство Count, определяемое для всех коллекций. В отличие от свойства Count, свойство Length доступно не только для чтения, но и для записи, а значит, с его помощью можно изменить размер коллекции типа BitArray.
Давайте рассмотрим пример использования класса BitArray:
using System;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void WriteBits(BitArray bits)
{
foreach (bool b in bits)
Console.Write(b ? 1 : 0);
Console.WriteLine("\n");
}
static void Main()
{
BitArray bits1 = new BitArray(10);
BitArray bits2 = new BitArray(10);
bits1.SetAll(false);
bits1.Set(2, true);
bits1[3] = true;
bits1[8] = true;
bits2 = bits1;
bits1 = bits1.Not();
Console.Write("bits1: ");
WriteBits(bits1);
Console.Write("bits2: ");
WriteBits(bits2);
Console.Write("NOT(bits1): ");
bits1.Not();
WriteBits(bits1);
Console.Write("bits1 AND bits2: ");
bits1.And(bits2);
WriteBits(bits1);
Console.ReadLine();
}
}
}
Структура BitVector32
Если необходимое количество бит известно заранее, то вместо BitArray можно использовать структуру BitVector32. Структура BitVector32 более эффективна, поскольку это тип значения, хранящий биты в стеке внутри целого числа. В единственном целом числе имеется место для 32 бит. Если нужно больше, можно применять множество значений BitVector32 или же BitArray. Класс BitArray при необходимости может расти, а структура BitVector32 лишена такой возможности.
Ниже перечислены члены структуры BitVector32, которые существенно отличаются от BitArray:
- Data
Свойство Data возвращает данные BitVector32 в виде целого числа.
- Item
Значение BitVector32 может быть установлено с использованием целого числа. Индексатор перегружен: получать и устанавливать значения можно с использованием маски или секции типа BitVector32.Section.
- CreateMask()
Статический метод, который позволяет создавать маску для доступа к определенным битам Bitvector32.
- CreateSelection()
Статический метод, который позволяет создавать несколько секций внутри 32 бит.
В приведенном ниже примере создается структура BitVector32 с помощью конструктора по умолчанию, при этом все 32 бита инициализируются false. Затем создаются маски для доступа к битам внутри битового вектора. Первый вызов CreateMask() создает маску для доступа к первому биту. После вызова CreateMask() значение bitl равно 1. Еще один вызов CreateMask() возвращает маску для доступа ко второму биту, которая равна 2. bit3 имеет значение 4 для доступа к биту номер 3. bit4 имеет значение 8 для доступа к биту номер 4.
Затем маски используются с индексатором для доступа к битам внутри вектора бит и соответствующей установки полей:
var bits1 = new BitVector32 () ;
int bit1 = BitVector32.CreateMask();
int bit2 = BitVector32.CreateMask(bit1);
int bit3 = BitVector32.CreateMask(bit2);
int bit4 = BitVector32.CreateMask(bit3);
int bit5 = BitVector32.CreateMask(bit4);
bits1[bit1] = true;
bits1[bit2] = false;
bits1[bit3] = true;
bits1[bit4] = true;
bits1[bit5] = true;
Console.WriteLine(bits1);