Фильтр Блума в Java с использованием Guava

1. Обзор

В этой статье мы рассмотрим конструкцию фильтра Bloom из библиотеки Guava . Фильтр Блума - это вероятностная структура данных с эффективным использованием памяти, которую мы можем использовать, чтобы ответить на вопрос , находится ли данный элемент в наборе .

  • Нет ложных негативов ** с фильтром Блума, поэтому, когда он возвращает false , мы можем быть на 100% уверены, что элемент отсутствует в наборе

Однако фильтр Блума может возвращать ложные срабатывания , поэтому, когда он возвращает true , существует высокая вероятность того, что элемент находится в наборе, но мы не можем быть уверены на 100%.

Для более глубокого анализа работы фильтра Блума вы можете перейти по ссылке this tutorial .

2. Maven Dependency

Мы будем использовать реализацию фильтра Bloom в Guava, поэтому давайте добавим зависимость guava :

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>22.0</version>
</dependency>

Последнюю версию можно найти на Maven Central .

3. Зачем использовать Bloom Filter?

Фильтр Bloom разработан так, чтобы быть компактным и быстрым . При его использовании мы можем указать вероятность ложноположительных ответов, которые мы можем принять , и, согласно этой конфигурации, фильтр Блума будет занимать как можно меньше памяти.

Благодаря такой экономии пространства фильтр Блума легко помещается в памяти даже для огромного количества элементов. Некоторые базы данных, включая Cassandra и Oracle, используют этот фильтр в качестве первой проверки перед переходом на диск или в кэш, например, когда поступает запрос на конкретный идентификатор.

Если фильтр возвращает отсутствие идентификатора, база данных может остановить дальнейшую обработку запроса и вернуться к клиенту. В противном случае он отправляется на диск и возвращает элемент, если он найден на диске.

4. Создание фильтра Блума

Предположим, что мы хотим создать фильтр Блума для до 500 Integers и что мы можем допустить вероятность ложных срабатываний в один процент (0,01).

Мы можем использовать класс BloomFilter из библиотеки Guava для достижения этой цели. Нам нужно передать количество элементов, которые мы ожидаем вставить в фильтр, и желаемую вероятность ложного срабатывания:

BloomFilter<Integer> filter = BloomFilter.create(
  Funnels.integerFunnel(),
  500,
  0.01);

Теперь давайте добавим несколько чисел в фильтр:

filter.put(1);
filter.put(2);
filter.put(3);

Мы добавили только три элемента и определили, что максимальное количество вставок будет 500, поэтому наш фильтр Bloom должен давать очень точные результаты Давайте проверим это с помощью метода mightContain () :

assertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();

assertThat(filter.mightContain(100)).isFalse();

Как следует из названия, мы не можем быть на 100% уверены, что данный элемент действительно находится в фильтре, когда метод возвращает true .

Когда mightContain () возвращает true в нашем примере, мы можем быть на 99% уверены, что элемент находится в фильтре, и есть вероятность того, что результат окажется ложноположительным. Когда фильтр возвращает false , мы можем быть на 100% уверены, что элемент отсутствует.

5. Насыщенный фильтр Блума

Когда мы проектируем наш фильтр Блума, важно, чтобы мы предоставили достаточно точное значение для ожидаемого количества элементов .

В противном случае наш фильтр будет возвращать ложные срабатывания с гораздо большей скоростью, чем нужно. Давайте посмотрим на пример.

Предположим, что мы создали фильтр с желаемой ложноположительной вероятностью в один процент и ожидаемыми элементами, равными пяти, но затем мы вставили 100 000 элементов:

BloomFilter<Integer> filter = BloomFilter.create(
  Funnels.integerFunnel(),
  5,
  0.01);

IntStream.range(0, 100__000).forEach(filter::put);

Поскольку ожидаемое количество элементов очень мало, фильтр будет занимать очень мало памяти.

Однако, поскольку мы добавляем больше элементов, чем ожидалось, фильтр становится перенасыщенным и имеет гораздо большую вероятность возврата ложноположительных результатов , чем требуемый один процент:

assertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();
assertThat(filter.mightContain(1__000__000)).isTrue();

Обратите внимание, что метод mightContatin () вернул true даже для значения, которое мы не вставляли ** в фильтр ранее.

6. Заключение

В этом кратком руководстве мы рассмотрели вероятностный характер структуры данных фильтра Блума - используя реализацию Guava .

Вы можете найти реализацию всех этих примеров и фрагментов кода в проекте GitHub .

Это проект Maven, поэтому его легко импортировать и запускать как есть.