Generalizing a choose operation
First and foremost, is there a way to generalize this function so that I can input any type of data and output the combination patterns on that?


Yes, if you supply the alphabet/element set as input to your function, and treat your output as indices into that alphabet.  Right now, your output is a list of numbers per combination (rather, a string, but see later).  This makes them feasible to use as an index into an arbitrary list.

In the least intrusive form, without modifying existing code, you can add this function that will do what you want.  (It's not efficient, though.)

/** Returns a list of all combinations over &lt;var&gt;alphabet&lt;/var&gt; of length &lt;var&gt;patternLength&lt;/var&gt;. */
public static &lt;T&gt; List&lt;List&lt;T&gt;&gt; choosePatterns(List&lt;T&gt; alphabet, int patternLength) {
    final int n = alphabet.size();
    String[] patterns = choosePatterns(n, n - patternLength, combi(n, patternLength));
    List&lt;List&lt;T&gt;&gt; retval = new ArrayList&lt;&gt;();
    for ( String pattern : patterns ) {
        List&lt;T&gt; list = pattern.chars()
                .map(Character::getNumericValue)
                .map(i -&gt; i - 1)
                .mapToObj(alphabet::get)
                .collect(Collectors.toList());
        retval.add(list);
    }
    return retval;
}

/** n \choose k */
static int combi(int n, int k) {
    int result = 1;
    for (int i = 0; i &lt; k; i++) {
        result *= (n-i);
        result /= (i+1);
    }
    return result;
}

/* Example usage */
public static void main(String[] args) {
    List&lt;String&gt; alphabet = Arrays.asList("C", "A", "T", "G");
    for ( List&lt;String&gt; pattern : choosePatterns(alphabet, 3) ) {
        System.out.println(pattern);
    }

    List&lt;?&gt; numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    for ( List&lt;?&gt; pattern : choosePatterns(numbers, 3) ) {
        System.out.println(pattern);
    }
}



  Secondly, are there ways to optimize these methods so it runs in the faster time possible?


Yes, if only because you're working with Strings/objects where you don't really need to.  But it depends on how far you want to take it.

On the surface, there are a number of inefficiencies that you can eliminate:


Working with List&lt;Integer&gt; rather than int[] : all your lists are fixed-size.  Working with arrays will allow you to use primitives, which will improve performance when making calculations.  (When not making calculations, this difference is still noticeable but much less pronounced.)
Returning String rather than int[] : if you return the indices in directly usuable form, you can avoid having to translate back and forth when using them, and also reduce the number of object allocations.  (Allocations are cheap but not free.)
data = new StringBuilder("") : it's typically cheaper (but not as 'clean') to wipe your current buffer than to allocate a new one.  For reasons beyond my comprehension, StringBuilder does not have a clear() method, but you can use delete(0, xyz.length()).  (Even better to just not use Strings, then this becomes moot.)


Depending on your use case, you can also consider a different approach (*).  Right now, you generate all possible combinations before returning.  If you don't need all combinations at the same time, this may be overkill.  (If you do, then making them all in one go is probably the most efficient.)



(*) Because the set of possible combinations is enumerable, you can assign a unique number to a combination, given an alphabet and a pattern length.  Meaning you can have a mapping function, i &rarr; combination(alphabet, k), as the base of an (spl)iterator or even a java.util.List implementation with a very efficient contains operation.  But that may be beyond the scope of this answer.