Сжатие / декомпрессия строк с использованием дефлятора - PullRequest
3 голосов
/ 03 марта 2012

Я хочу сжать / распаковать и сериализовать / десериализовать содержимое строки. Я использую следующие две статические функции.

/**
 * Compress data based on the {@link Deflater}.
 * 
 * @param pToCompress
 *            input byte-array
 * @return compressed byte-array
 * @throws NullPointerException
 *             if {@code pToCompress} is {@code null}
 */
public static byte[] compress(@Nonnull final byte[] pToCompress) {
    checkNotNull(pToCompress);

    // Compressed result.
    byte[] compressed = new byte[] {};

    // Create the compressor.
    final Deflater compressor = new Deflater();
    compressor.setLevel(Deflater.BEST_SPEED);

    // Give the compressor the data to compress.
    compressor.setInput(pToCompress);
    compressor.finish();

    /*
     * Create an expandable byte array to hold the compressed data.
     * You cannot use an array that's the same size as the orginal because
     * there is no guarantee that the compressed data will be smaller than
     * the uncompressed data.
     */
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream(pToCompress.length)) {
        // Compress the data.
        final byte[] buf = new byte[1024];
        while (!compressor.finished()) {
            final int count = compressor.deflate(buf);
            bos.write(buf, 0, count);
        }

        // Get the compressed data.
        compressed = bos.toByteArray();
    } catch (final IOException e) {
        LOGWRAPPER.error(e.getMessage(), e);
        throw new RuntimeException(e);
    }


    return compressed;
}

/**
 * Decompress data based on the {@link Inflater}.
 * 
 * @param pCompressed
 *            input string
 * @return compressed byte-array
 * @throws NullPointerException
 *             if {@code pCompressed} is {@code null}
 */
public static byte[] decompress(@Nonnull final byte[] pCompressed) {
    checkNotNull(pCompressed);

    // Create the decompressor and give it the data to compress.
    final Inflater decompressor = new Inflater();
    decompressor.setInput(pCompressed);

    byte[] decompressed = new byte[] {};

    // Create an expandable byte array to hold the decompressed data.
    try (final ByteArrayOutputStream bos = new ByteArrayOutputStream(pCompressed.length)) {
        // Decompress the data.
        final byte[] buf = new byte[1024];
        while (!decompressor.finished()) {
            try {
                final int count = decompressor.inflate(buf);
                bos.write(buf, 0, count);
            } catch (final DataFormatException e) {
                LOGWRAPPER.error(e.getMessage(), e);
                throw new RuntimeException(e);
            }
        }
        // Get the decompressed data.
        decompressed = bos.toByteArray();
    } catch (final IOException e) {
        LOGWRAPPER.error(e.getMessage(), e);
    }

    return decompressed;
}

Тем не менее, по сравнению с несжатыми значениями, это на несколько порядков медленнее, даже если я кеширую распакованный результат, а значения распаковываются только, если контент действительно нужен.

То есть он используется для устойчивой древовидной структуры, подобной DOM, и XPath-запросов, которые заставляют декомпрессию значений String примерно в 50 раз, если не больше, медленнее (на самом деле не тестируются, просто выполняются модульные тесты). Мой ноутбук даже зависает после некоторых модульных тестов (каждый раз проверял его примерно 5 раз), потому что Eclipse больше не отвечает из-за интенсивного дискового ввода-вывода, а что нет. Я даже установил уровень сжатия на Deflater.BEST_SPEED, тогда как другие уровни сжатия могли бы быть лучше, возможно, я предоставляю параметр конфигурации, который можно установить для resources. Возможно, я что-то напутал, потому что раньше не использовал дефлятор. Я даже сжимаю контент только там, где длина строки> 10.

Edit: После рассмотрения извлечения экземпляра Deflater в статическое поле создается впечатление, что создание экземпляра deflater и inflater является очень дорогостоящим, так как узкое место в производительности устранено и, возможно, без микробенчмарков или тому подобного я могу ' Я не вижу потери производительности :-) Я просто сбрасываю дефлятор / инфлятор перед использованием нового входа.

Ответы [ 2 ]

2 голосов
/ 03 марта 2012

Как вы рассматривали возможность использования API более высокого уровня, например Gzip.

Вот пример сжатия:

public static byte[] compressToByte(final String data, final String encoding)
    throws IOException
{
    if (data == null || data.length == 0)
    {
        return null;
    }
    else
    {
        byte[] bytes = data.getBytes(encoding);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream os = new GZIPOutputStream(baos);
        os.write(bytes, 0, bytes.length);
        os.close();
        byte[] result = baos.toByteArray();
        return result;
    }
}

Вот пример распаковки:

public static String unCompressString(final byte[] data, final String encoding)
    throws IOException
{
    if (data == null || data.length == 0)
    {
        return null;
    }
    else
    {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        GZIPInputStream is = new GZIPInputStream(bais);
        byte[] tmp = new byte[256];
        while (true)
        {
            int r = is.read(tmp);
            if (r < 0)
            {
                break;
            }
            buffer.write(tmp, 0, r);
        }
        is.close();

        byte[] content = buffer.toByteArray();
        return new String(content, 0, content.length, encoding);
    }
}

Благодаря этому мы получаем очень хорошую производительность и степень сжатия.

Zip API также является опцией.

0 голосов
/ 12 августа 2013

Ваши комментарии - правильный ответ.

В общем, если метод будет использоваться часто, вы хотите исключить любые выделения и копирование данных. Это часто означает удаление инициализации экземпляра и других настроек либо для статических переменных, либо для конструктора.

Использование статики проще, но вы можете столкнуться с проблемами на всю жизнь (например, как узнать, когда нужно очищать статику - существуют ли они вечно?).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...