Любой метод/метод, чтобы взять большую строку и вернуть InputStream?

Я ищу некоторый класс/метод util, чтобы взять большой String и вернуть InputStream.

Если String мал, я могу просто сделать:

InputStream is = new ByteArrayInputStream(str.getBytes(<charset>));

Но когда размер String большой (1 МБ, 10 МБ или более), массив байтов от 1 до 2 раз (или более?), такой же большой, как моя строка, выделяется на месте. (И поскольку вы не знаете, сколько байтов выделяется точно до того, как все кодирование будет выполнено, я думаю, что должны быть другие массивы/буферы, выделенные до выделения финального байтового массива).

У меня есть требования к производительности и вы хотите оптимизировать эту операцию.

В идеале, я думаю, класс/метод, который я ищу, будет кодировать символы "на лету" на одном небольшом блоке за раз, когда потребляется InputStream, и поэтому не происходит большой всплеск распределения памяти.

Глядя на исходный код apache commons IOUtils.toInputStream(..), я вижу, что он также преобразует String в большой массив байтов за один раз.

И StringBufferInputStream устарел и не выполняет работу должным образом.

Есть ли такой класс/метод использования из любого места? Или я могу написать пару строк кода, чтобы сделать это?

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

Я не думаю, что другие люди ищут что-то подобное. Я что-то придумываю где-то?

Я начал писать пользовательский класс для этого, но остановился бы, если бы было лучшее/правильное/правильное решение/исправление для моей потребности.

Ответ 1

Встроенные библиотеки Java предполагают, что вам нужно будет только перейти от символов к байтам на выходе, а не вводить. Однако библиотеки IO Apache Commons IO ReaderInputStream, которые могут обернуть StringReader, чтобы получить то, что вы хотите.

Ответ 2

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

В любом случае вы можете попробовать следующее:

public static InputStream largeStringToBytes(final String tooLarge,
    final Charset charset)
{
    final CharsetEncoder encoder = charset.newEncoder()
        .onUnmappableCharacter(CodingErrorAction.REPORT);
    final ByteBuffer buf = charset.encode(CharBuffer.wrap(tooLarge));
    return new ByteArrayInputStream(buf.array());
}

Ответ 3

Если вы передаете большую строку в качестве параметра, тогда память уже выделена. Строка, которая не может быть даже вложена в стек (в большинстве случаев максимальный размер стека равен 1 МБ), поэтому он получает выделение в куче только для передачи его в качестве параметра. Единственный способ избежать этого - создать дерево на диске, на котором вы побросали назад, когда вы шли по дереву. Если у вас есть несколько больших строк, возможно, они могут индексировать их в Trie или DAWG и ходить по этой структуре. Это устранит многие повторяющиеся символы между строками. Но, мне нужно будет узнать больше о том, что представляют строки для дальнейшего содействия.

Ответ 4

Реализовать собственный поток, поддерживаемый строкой:

class StringifiedInputStream extends InputStream {

    private int idx=0;
    private final String str;
    private final int len;

    StringifiedInputStream(String str) {
        this.str = str;
        this.len = str.length();
    }

    @Override
    public int read() throws IOException {
        if(idx>=len)
            return -1;

        return (byte) str.charAt(idx++);
    }
}

Это медленно, но он передает байты без дублирования массива байтов. Добавьте метод 3-arg к этой реализации, если скорость является проблемой.