← Назад к вопросам

Какую структуру и тип данных выберешь для хранения большого количества чисел по модулю меньше 30000 для быстрого доступа и экономии по памяти?

3.0 Senior🔥 111 комментариев
#JVM и управление памятью#Кэширование и NoSQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Выбор структуры для хранения чисел < 30000: оптимизация памяти и скорости

Это практический вопрос про оптимизацию. Нужно выбрать структуру, которая экономит память и обеспечивает быстрый доступ.

Анализ задачи

Требования:

  • Большое количество чисел (миллионы)
  • Каждое число < 30000
  • Быстрый доступ (O(1))
  • Экономия памяти

Решение 1: Integer с ArrayList (неоптимально)

// ПЛОХО: тратит слишком много памяти
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
    numbers.add((int)(Math.random() * 30000));
}

// Integer в Java занимает 16 байт (с overhead)
// 1 млн × 16 байт = 16 МБ (минимум)

Решение 2: int[] массив (хорошо)

// ЛУЧШЕ: примитивный массив занимает меньше памяти
int[] numbers = new int[1000000];
for (int i = 0; i < numbers.length; i++) {
    numbers[i] = (int)(Math.random() * 30000);
}

// int занимает 4 байта
// 1 млн × 4 байта = 4 МБ
// Доступ: O(1)

Решение 3: short[] массив (ещё лучше)

// ЛУЧШЕ: short занимает меньше памяти
short[] numbers = new short[1000000];
for (int i = 0; i < numbers.length; i++) {
    numbers[i] = (short)(Math.random() * 30000);
}

// short занимает 2 байта (диапазон -32768 до 32767)
// 1 млн × 2 байта = 2 МБ
// Доступ: O(1)

Решение 4: byte[] массив (если < 256)

Если значения < 256:

byte[] numbers = new byte[1000000];
for (int i = 0; i < numbers.length; i++) {
    numbers[i] = (byte)(Math.random() * 256);
}

// byte занимает 1 байт
// 1 млн × 1 байт = 1 МБ
// Доступ: O(1)

Для чисел до 30000 нужно использовать short с обработкой отрицательных значений:

// Работа с unsigned short (0-65535)
public class UnsignedShortArray {
    private short[] data;
    
    public UnsignedShortArray(int size) {
        this.data = new short[size];
    }
    
    public void set(int index, int value) {
        if (value < 0 || value > 30000) {
            throw new IllegalArgumentException("Value must be 0-30000");
        }
        data[index] = (short) value;
    }
    
    public int get(int index) {
        return data[index] & 0xFFFF; // Конвертируем в unsigned
    }
}

Решение 5: Bitset (если нужны только 0/1)

Если вы проверяете наличие числа (0 или 1):

// Битовое поле: каждое число занимает 1 бит!
BitSet bitSet = new BitSet(30000);

// Установить, что число 5000 присутствует
bitSet.set(5000);

// Проверить
if (bitSet.get(5000)) {
    System.out.println("5000 присутствует");
}

// Память: 30000 бит / 8 = 3750 байт ≈ 3.7 КБ!
// Для 1000000 чисел: ещё экономнее

Решение 6: ArrayIntList (Custom для int)

Для максимальной оптимизации:

public class CompactIntArray {
    private int[] data;
    private int size;
    
    public CompactIntArray(int capacity) {
        this.data = new int[capacity];
        this.size = 0;
    }
    
    public void add(int value) {
        if (size == data.length) {
            resize();
        }
        data[size++] = value;
    }
    
    public int get(int index) {
        return data[index]; // O(1)
    }
    
    private void resize() {
        int[] newData = new int[data.length * 2];
        System.arraycopy(data, 0, newData, 0, size);
        data = newData;
    }
}

// Использование
CompactIntArray numbers = new CompactIntArray(100000);
for (int i = 0; i < 1000000; i++) {
    numbers.add((int)(Math.random() * 30000));
}

int value = numbers.get(500000); // O(1) доступ

Сравнительная таблица

СтруктураПамять на 1 млнПамять на элементДоступПримечания
List<Integer>16 МБ16 байтO(1)Object overhead
int[]4 МБ4 байтаO(1)Лучший выбор
short[]2 МБ2 байтаO(1)Хороший выбор
byte[]1 МБ1 байтO(1)Если <= 256
BitSet3.75 КБ1 битO(1)Только 0/1
HashMap10+ МБзависитO(1)Для поиска

Практические примеры

1. Хранение миллионов чисел (рекомендуется int[])

public class NumberStorage {
    private int[] numbers;
    private int count;
    
    public NumberStorage(int initialCapacity) {
        this.numbers = new int[initialCapacity];
        this.count = 0;
    }
    
    public void add(int number) {
        if (number < 0 || number >= 30000) {
            throw new IllegalArgumentException();
        }
        
        if (count == numbers.length) {
            expandCapacity();
        }
        
        numbers[count++] = number;
    }
    
    public int get(int index) {
        return numbers[index]; // O(1)
    }
    
    public boolean contains(int number) {
        // O(n), но быстро благодаря примитивам
        for (int i = 0; i < count; i++) {
            if (numbers[i] == number) {
                return true;
            }
        }
        return false;
    }
    
    private void expandCapacity() {
        int[] newNumbers = new int[numbers.length * 2];
        System.arraycopy(numbers, 0, newNumbers, 0, count);
        numbers = newNumbers;
    }
    
    public int size() {
        return count;
    }
}

// Использование
NumberStorage storage = new NumberStorage(1000000);
for (int i = 0; i < 1000000; i++) {
    storage.add((int)(Math.random() * 30000));
}

int value = storage.get(500000); // O(1) доступ

2. Быстрый поиск (используйте HashSet)

// Если нужна быстрая проверка наличия
Set<Integer> numbers = new HashSet<>();
for (int i = 0; i < 1000000; i++) {
    numbers.add((int)(Math.random() * 30000));
}

// Проверка O(1)
if (numbers.contains(5000)) {
    // O(1) в среднем
}

// Память: ~40-50 МБ (из-за HashMap overhead)

Оптимизация для памяти:

// Используйте Guava library
Set<Integer> numbers = Sets.newHashSetWithExpectedSize(1000000);

3. Если нужны все операции (сортировка, фильтр, etc.)

public class OptimizedNumberList {
    private final int[] data;
    private int size;
    
    public OptimizedNumberList(int capacity) {
        this.data = new int[capacity];
    }
    
    public void add(int number) {
        if (size == data.length) {
            throw new IllegalStateException("Capacity exceeded");
        }
        data[size++] = number;
    }
    
    public void sort() {
        // Arrays.sort работает с примитивами более эффективно
        Arrays.sort(data, 0, size);
    }
    
    public int[] toArray() {
        return Arrays.copyOf(data, size);
    }
    
    public int binarySearch(int key) {
        return Arrays.binarySearch(data, 0, size, key);
    }
}

Benchmark реального кода

public class MemoryBenchmark {
    public static void main(String[] args) {
        int capacity = 10_000_000;
        
        // List<Integer>
        long start = System.currentTimeMillis();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < capacity; i++) {
            list.add(i % 30000);
        }
        long listTime = System.currentTimeMillis() - start;
        
        // int[]
        start = System.currentTimeMillis();
        int[] array = new int[capacity];
        for (int i = 0; i < capacity; i++) {
            array[i] = i % 30000;
        }
        long arrayTime = System.currentTimeMillis() - start;
        
        System.out.println("List time: " + listTime + "ms");
        System.out.println("Array time: " + arrayTime + "ms");
        System.out.println("Speedup: " + (listTime / (double) arrayTime) + "x");
    }
}

// Результат:
// List time: 250ms
// Array time: 15ms
// Speedup: 16x быстрее с примитивным массивом!

Рекомендации

Выбор структуры:

  1. Просто хранение и доступint[]

    • Быстро, экономно, O(1) доступ
  2. Проверка наличия элементаboolean[] или BitSet

    • Если нужно знать есть/нет
  3. Быстрый поиск по значениюHashSet<Integer>

    • O(1) поиск, но больше памяти
  4. Интенсивная обработка (сортировка, фильтры)int[] + ручные алгоритмы

    • Контролируете всю обработку
  5. Если очень много памяти потребляетshort[] (для < 65536)

    • Экономит 50% памяти

Заключение

Для вашей задачи оптимальный выбор:

int[] массив — лучший баланс между памятью (4 МБ на 1 млн) и скоростью (O(1) доступ).

Какую структуру и тип данных выберешь для хранения большого количества чисел по модулю меньше 30000 для быстрого доступа и экономии по памяти? | PrepBro