private void quickSort(List<T> list, int startIndex, int endIndex) {
    // BASE CASE: List contains one or fewer elements or the indexes are out of bounds.
    // This indicates that the list is sorted as we have no out of order elements in a zero or one
    // element list.
    if (endIndex <= startIndex || endIndex < 0 || endIndex >= list.size()) {
      return;
    }

    // RECURSIVE CASE: The current list can still be divided (i.e., partitioned) into sublists.
    // Partition the lists into two lists and return the index of the element
    // that served as the pivot (i.e., the element that divided the two lists). The two lists
    // will consist of all elements smaller than the partition (one list) and all elements larger
    // than the partition (second list).
    int partionedIndex = partitionListIntoTwo(list, startIndex, endIndex);

    // With the list sorted into two halves around a pivot index, sort each half individually
    // using quick sort. This is Step 3 described in the class comments.
    quickSort(list, startIndex, partionedIndex - 1);
    quickSort(list, partionedIndex + 1, endIndex);
  }