Idiomatic approach to applying a keyword replace algorithm to a Java 8 Stream
Bug:

Calling toString  repeatedly will append the last keyword (or the buffer) multiple times. This is not really in line with how this should work.

Conventions:

Java coding conventions state that member names should be lowerCamelCase. the fields related to the keyword trie do not follow that convention.

Another convention that you seem to disregard here is that Tries usually enable quick searching through strings by virtue of allowing skipping characters before doing the next comparison. As such naming the KeywordTrie as "Trie" is not really correct.

Minor simplifications &amp; fixes:

You should specify explicit visibilities on all your fields. It's almost never appropriate to use the default visibility in java. package-private is almost always wrong.

The names you're using are somewhat wordy for my own taste as well.

In addition you can initialize fields in a field initializer instead of the constructor to make the ctor simpler to read. Consider:

public class Replacer [...] {
    private StringBuffer out = new StringBuffer();
    private StringBuffer buffer = new StringBuffer();
    private KeywordTrieNode currentNode;
    private KeywordTrieNode trieRoot;


Design issues:

A consumer should not return a result. That's not what they do. They take an element and that's it.

What you have here is a Collector. Something that takes a Stream and collapses it into a value. Let's make sure we use the correct interface here:

public class Replacer implements Collector&lt;Character, StringBuilder, String&gt; {


This in turn gives us the following functions to implement:

BiConsumer&lt;StringBuffer, Character&gt; accumulator()
Set&lt;Consumer.Characteristics&gt; characteristics()
BinaryOperator&lt;StringBuffer&gt; combiner()
Function&lt;StringBuffer, String&gt; finisher()
Supplier&lt;StringBuffer&gt; supplier()


Let's start with the important stuff that governs how the Collector will be used by the Stream implementation. Our collector is Ordered, Sequential and changes stuff when the finisher is invoked. This means that none of the Characteristics for optimization apply:

public Set&lt;Collector.Characteristics&gt; characteristics() { 
    return Collections.emptySet(); 
}


Now we can deal with the stuff that we need to set up:

public BinaryOperator&lt;StringBuffer&gt; combiner() {
    // explicitly doesn't do anything, supplier always returns the same object
    return (a, b) -&gt; a;
}

public Supplier&lt;StringBuffer&gt; supplier() {
    return () -&gt; {
        return out;
    };
}


Note that I assume we're using the same private fields (and constructor) as in the question, because they seem like an acceptable encapsulation of the problem you're trying to solve.

Now we need to implement the accumulation, this is basically your "accept":

public BiConsumer&lt;StringBuffer, Character&gt; accumulator() {
    return (out, character) -&gt; {
        KeywordTrieNode next = currentNode.get(character);
        if (next != null) {
            buffer.append(character);
            currentNode = next;
        } else {
            String keyword = currentNode.get();
            out.append(keyword == null ? buffer : keyword)
               .append(character);
            buffer = new StringBuilder();
            currentNode = trieRoot;
        }
    };
};

public Function&lt;StringBuffer, String&gt; finisher() {
    return (out) -&gt; {
        String keyword = currentNode.get();
        out.append(keyword == null ? buffer : keyword);
        return out.toString();
    };
}


With this implementation we can rewrite your replace function to look as follows:

public String replace(IntStream chars) {
     return chars.collect(new Replacer(keywordTrieRoot));
}