Write a Java class "Sort" which implements the BubbleSort algorithm
No unnecessary math


        if (leftIdx &lt; 0 || leftIdx &gt; list.length - 1 
                || rightIdx &lt; 0 || rightIdx &gt; list.length - 1) 



You could simplify this to 

        if (leftIdx &lt; 0 || leftIdx &gt;= list.length 
                || rightIdx &lt; 0 || rightIdx &gt;= list.length) 


Because if either Idx is equal to list.length, it is greater than list.length - 1.  

Don't do extra work


        for (int i = list.length; i &gt; 1; i--) 
        {                
            for (int j = leftIdx; j &lt; rightIdx; j++) 



You're doing more passes than necessary.  

        for (int i = rightIdx; i &gt; leftIdx; i--) 
        {                
            for (int j = leftIdx; j &lt; i; j++) 


This works by bubbling the largest element to the end.  Once that's done, we don't have to review the end again.  So we can decrease i, which points to the end.  

Bubble Sort best case


        for (int i = list.length; i &gt; 1; i--) 
        {                
            for (int j = leftIdx; j &lt; rightIdx; j++) 
            {                   
                if (list[j] &gt; list[j + 1]) 
                {
                    int tmp = list[j];

                    list[j] = list[j + 1];
                    list[j + 1] = tmp;
                }                    
            }
        }



One of the advantages of bubble sort is that it is possible to get a best case of linear time.  However, this code doesn't do that.  It's always quadratic, regardless of whether the data is already sorted.  

Consider 

        for (int i = rightIdx; i &gt; leftIdx; i--) 
        {
            Integer tmp = null;

            for (int j = leftIdx; j &lt; i; j++) 
            {                   
                if (list[j] &gt; list[j + 1]) 
                {
                    tmp = list[j];

                    list[j] = list[j + 1];
                    list[j + 1] = tmp;
                }                    
            }

            if (tmp == null)
            {
                break;
            }
        }


Now if we never swap, then tmp is null and we stop processing.  If the list is sorted, that happens on the first outer iteration and we only go through the list once.  If the code is almost sorted but not quite, it could happen after just a few iterations.  

If you add data at the end a lot, note that reversing the sort so that it bubbles the smallest forward rather than the largest back would help.  

StringBuilder


        String ret = "";

        for (int i = start; i &lt;= end; i++) 
        {
            ret += list[i] + ", ";
        }

        return "[ " + ret.substring(0, ret.length() - 2) + " ]";



This should use a StringBuilder instead of string concatenation.  

        String Builder ret = new StringBuilder("[ ");

        for (int i = start; i &lt; end; i++) 
        {
            ret.append(list[i]).append(", ");
        }
        ret.append(list[end]).append(" ]");

        return ret.toString();


Now rather than creating new strings each time, it just expands the StringBuilder as needed.  

I also changed it so that the substring call is unnecessary.  Since we're manually iterating anyway, we can just not do the last element inside the loop.  That way we can handle it separately outside.  

The substring trick makes more sense when we're automating the iteration such that we can't control the bounds.  But that's not the case here.