• Collections Framework中的算法(之二)


      从本篇开始我们讲述Collections中的一些算法的源代码!本篇主要讲述与排序相关的一些方法,如:排序、反序、反序比较器、乱序、最大值和最小值等。

     

    一、头及一些与算法相关的属性

    package java.util;

    import java.io.Serializable;

    import java.io.ObjectOutputStream;

    import java.io.IOException;

    import java.lang.reflect.Array;

     

    public class Collections {

        private Collections() {    }                           //私有构造器,表明是一个工具类

     

    // 算法相关的成员变量,它们的名字都有一个_THRESHOLD表示它是一个门槛

    //当集合中的数据大于此门槛时,选择某个算法,而小于此门槛时选择另外一种算法

    //记住它的意义,以及为什么使用即可。

        private static final int BINARYSEARCH_THRESHOLD   = 5000;

        private static final int REVERSE_THRESHOLD        =   18;

        private static final int SHUFFLE_THRESHOLD        =    5;

        private static final int FILL_THRESHOLD           =   25;

        private static final int ROTATE_THRESHOLD         =  100;

        private static final int COPY_THRESHOLD           =   10;

        private static final int REPLACEALL_THRESHOLD     =   11;

        private static final int INDEXOFSUBLIST_THRESHOLD =   35;

     

    二、排序

           Java中的排序主要在Arrays类中,我们的Collections中的排序是将当前List通过Collection提供的toArray作为桥梁转变为Array,然后使用Arrays的排序方法!这就是两者排序的联系!

    2.1使用默认的比较器排序

           //排序在程序设计过程中经常用到!

           //Java集合框架,在Arrays类中提供了各种不同类型数组的排序算法

           //Arrays类中的排序,效率很高。请阅读Arrays的源代码

     

        public static <T extends Comparable<? super T>> void sort(List<T> list) {

             Object[] a = list.toArray();                                  //将集合转变为数组

             Arrays.sort(a);                                                   //运用数组的排序方法排序,采用二分发排序

             ListIterator<T> i = list.listIterator();            //这里不可以使用Collectioniterator方法,没有set方法

             for (int j=0; j<a.length; j++) {                     //主要使用ListIterator接口的set方法

                 i.next();      

                 i.set((T)a[j]);                                              //排好叙的数组依次赋给集合元素

             }

    }

     

    2.2 使用自定义的比较器排序

             //关键在Arrays中如何使用比较器的,有机会再讲!你完全可以自己阅读!

        public static <T> void sort(List<T> list, Comparator<? super T> c) {    //使用自定义的比较器排序

         Object[] a = list.toArray();

         Arrays.sort(a, (Comparator)c);                                  //c就是我们自定义的比较器

    //调用数组二分发排序方法,通过比较起比较各元素

         ListIterator i = list.listIterator();

         for (int j=0; j<a.length; j++) {

             i.next();

             i.set(a[j]);

         }

        }

     

    三、反序比较器

           //sortmaxmin方法中我们看到使用默认比较方法和自定义比较器的完成上面功能的过程!

           //在上一篇的例子中也自己写了两个自定义的比较器!可能你还是不太明白,请暂时放放吧!

           //在这里会讲述一个特殊的比较器!将默认的比较方法反向,和将自定义的比较器反序

           //这就是反向比较器。

     

    public static <T> Comparator<T> reverseOrder() {

                                //Coolections中调用这个静态的方法,创建一个比较器

            return (Comparator<T>) REVERSE_ORDER;                           //返回一个对象

        }

     

        private static final Comparator REVERSE_ORDER = new ReverseComparator();

             //这个对象是所有反序比较器共有的!Flighweight的应用

     

        private static class ReverseComparator<T>    implements Comparator<Comparable<Object>>, Serializable {

                      private static final long serialVersionUID = 7207038068494060240L;

            public int compare(Comparable<Object> c1, Comparable<Object> c2) {

                return c2.compareTo(c1);                        //这是该比较器的主要方法,调换原先的比较次序

            }

        }

     

        public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {              //使用自定义比较器的反序比较器

            if (cmp == null)    return new ReverseComparator();  // Unchecked warning!!

             return new ReverseComparator2<T>(cmp);

        }

     

        private static class ReverseComparator2<T> implements Comparator<T>, Serializable {

            private static final long serialVersionUID = 4374092139857L;

            private Comparator<T> cmp;

            ReverseComparator2(Comparator<T> cmp) {

                assert cmp != null;

                this.cmp = cmp;

            }

            public int compare(T t1, T t2) {

                return cmp.compare(t2, t1);                                        //调换原先比较器的比较次序

            }

        }

             到目前为止我们排序的方法比较多了!以下是几种排序及其注意点:

    1.        首先这些加入集合List中的元素,必须实现Comparable接口,实现其方法compareTo(Object)方法;

    2.        我们可以使用默认的比较方法排序,Collections.sort(List);

    3.        我们可以使用默认比较器的反序比较器,Collections.sort(List, Collections. reverseOrder());能明白吧!他也是一个比较器,只是是一个特殊的比较器!领会吧!

    4.        我们可以采用自定义的比较器,象我们上篇讲的两个年龄的比较器和姓名的比较器;Collections.sort(List,MyComparator)

    5.        我们可以在有了一个自定义的比较器时,对器排序不是很满意,想反向一下!这时也可以使用方向比较器;Collections.sort(List, Collections. reverseOrder(MyComparator))。我现在告诉你这也是一个装饰器模式你能理解吗??ReverseComparator2是不是啊!理解理解Decorator模式的场景,意图看看!还是要告诉你模式是一种观念,不是一成不变的,领会吧!在必要的时候使用他!

     

    四、最小值

           求最大值、最小值很常见吧!我在《由一个简单的程序谈起》中建立的那个小程序,使用了上面的sort方法,然后取出最后一个对象,其实就是一个求最大值的过程。不过Collections为我们提供了两种求最值的方法哦!

    6.1使用对象默认的比较器求最小值

           //求最小值的思路是:刚开始取第一个元素作为最小值,然后依次遍列,

           //有比他小的就替换当前这个最小值,直到所有的遍列完成,

           //那个最终的最小值就是整个List的最小值

           //唉!Who laughs lastwo laughs better

           //使用下一个对象的compateTo方法与当前对象比较

        public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) {

             Iterator<? extends T> i = coll.iterator();                                //得到集合的遍历器!不懂就查看遍历器模式吧!

             T candidate = i.next();                                                          //取出第一个元素与其它元素比较!为什么?

            while(i.hasNext()) {

                 T next = i.next();

                 if (next.compareTo(candidate) < 0)                            candidate = next;

                       //使用默认的比较方法!具体怎么用,见前一篇文章!

             }

             return candidate;

        }

     

     

    6.2 使用自定义的比较器求最小值

        public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {

            if (comp==null)        return (T)min((Collection<SelfComparable>) (Collection) coll);

                       //上面一句的意思是:没有比较器使用自身的比较方法!当然调用上面的方法哦!

                      Iterator<? extends T> i = coll.iterator();

                      T candidate = i.next();

            while(i.hasNext()) {

                 T next = i.next();

                 if (comp.compare(next, candidate) < 0)  candidate = next;         

                       //两者的差别看到了吧!使用默认的比较方法用next.compareTo(candidate) < 0

                       //看仔细了!所有的细节都在这里

             }

             return candidate;

    }

     

        private interface SelfComparable extends Comparable<SelfComparable> {}                   // 辅助的接口而已

             求最小值的思路我们可以明显看出使用自身的比较器和自定义比较器的差别了!你仔细都这两个方法吧!

    五、最大值

    //和求最小值同样的思路,我就不罗嗦了!

    10.1使用对象默认的比较器求最大值

        public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {

             Iterator<? extends T> i = coll.iterator();

             T candidate = i.next();

            while(i.hasNext()) {

                 T next = i.next();

                 if (next.compareTo(candidate) > 0)

                       candidate = next;

             }

             return candidate;

        }

     

    10.2使用自定义的比较器求最大值

        public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {

            if (comp==null)       return (T)max((Collection<SelfComparable>) (Collection) coll);

             Iterator<? extends T> i = coll.iterator();

             T candidate = i.next();

            while(i.hasNext()) {

                 T next = i.next();

                 if (comp.compare(next, candidate) > 0)

                       candidate = next;

             }

             return candidate;

        }

             //补充一下哦!其实在Collections中当你对这个List排序后,求最值也不需要这么麻烦,

             //你得到排序后List的最后一个和最前面一个不就是最大值和最小值吗!

             //仅仅要得到最大值和最小值,建议使用这个方法!为什么啊?

             //两者性能相差不少。我们这个方法只要遍列一次,比较一次!

             //先排序然后在得到最值有以下几个步骤:遍列一次得到一个数组,数组遍列且比较一次

             //得到一个排序好的数组,再遍列一次插入List,几倍的差距吧!

             //凡是多动脑!Use your head  and you’ll find a way 

     

    六、反序

           //List中的元素的次序完全反过来!

        public static void reverse(List<?> list) {

            int size = list.size();

            if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {               

                                         //根据元素的个数和List的类型决定使用何种交换次序的策略

                for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)              //从第一个开始依次交换头和对应尾的持续

                    swap(list, i, j);                                                              //交换indexij的元素

            } else {

                ListIterator fwd = list.listIterator();

                ListIterator rev = list.listIterator(size);

                for (int i=0, mid=list.size()>>1; i<mid; i++) {                        //一共移动元素个数的1/2

                                          Object tmp = fwd.next();                                              //得到前端的第i个元素

                    fwd.set(rev.previous());                                              //将前端i的元素设置为第size-i个元素

                    rev.set(tmp);                                                                //将后端size-i的元素设置为第i个元素

                }

            }                                                                                                     //简单吧!

        }

     

             //这个反序与排序的反序又有较大的差别!自己研究吧!

     

    七、乱序

             //经常用的变态的方法哦!使用循环向List中插入和循环变量一致的Integer对象这是网络上的例子

             //然后给你讲其它的!变态吧!本来有次序的,乱序一下,再给你写个自定义的排序器排序一下!

        public static void shuffle(List<?> list) {                                                     //List中的元素打乱次序

            shuffle(list, r);                                                                                  //使用一个随机发生器

    }

     

        private static Random r = new Random();                                             //随机发生器

     

        public static void shuffle(List<?> list, Random rnd) {                     //乱序的核心方法

            int size = list.size();

            if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {        //根据门槛条件及类型选择策略

                for (int i=size; i>1; i--)                                                                    //将第i个元素和其它任何元素反序

                    swap(list, i-1, rnd.nextInt(i));

            } else {

                Object arr[] = list.toArray();                                                            //对象太多转变为数组处理

                for (int i=size; i>1; i--)

                    swap(arr, i-1, rnd.nextInt(i));

                ListIterator it = list.listIterator();

                for (int i=0; i<arr.length; i++) {                                                        //将乱序后的数组的值依次插入List

                    it.next();

                    it.set(arr[i]);

                }

            }

        }

     

        public static void swap(List<?> list, int i, int j) {                                        //List中的第ij的元素换序

             final List l = list;

             l.set(i, l.set(j, l.get(i)));                                                                              //这个明白吧!List接口的基本方法

        }

     

        private static void swap(Object[] arr, int i, int j) {                                      //将数组的ij的元素互换次序

            Object tmp = arr[i];

            arr[i] = arr[j];

            arr[j] = tmp;

        }

             //吃饱了,没事撑着的方法。不过在某些场合还是很有用的!

     

           手头有个清华大学严蔚敏和吴伟民的《数据结构》pascal语言版的!一本经典的数据结构的教程,第十章、第十一章就是排序!第九章就是查找!下面我们就会研究Collections中的查找相关的知识!其实数据结构这们课不要太基础哦!就是一些堆、栈、队列、表、树、图以及一些排序和查找算法!不会这个可能干其它也很不容易!他是你从事IT行业的基础!不信你去问问严老师!这是个基础,在实际项目中,可能要建立很多比这个复杂很多的数据结构!不信啊!JDBC中的ResultSet就极其明显是一个数据结构!多看看吧!

           本来今天不想写的,有兄弟在QQ里说:“你写得太慢了,我都看完了!”有压力了我!几天挺累的,可不能让你们的写作枪手崩溃哦!毕竟能写的东西太多太多了!一切才刚刚开始啊!

           唉!一转眼,博客已经开了近一个月!这一个月我写了几十篇,有一次看完了Collections FrameworkSourceCode,同时看完了IO包中除ObjectIn/OutputStream相关的类!你有干了写这么呢??总结总结吧!每天都有什么收获啊!每个月都有什么收获啊!人吗,总得一天比一天强吧!要不然活着还有什么意义啊!

           元旦要到了!我也要放假了!提前祝贺大家:新年好!新年新气象!学习进步!家庭和谐!

           今年没好好干的明年可得好好干!没找到工作的学生可以继续找!看看http://www.java-cn.com/上的招聘信息吧!也有不少月薪8k以上的啊!只要你有能耐!钱终究会有的!只有你有那个能力,才可能拿到那个薪水!学好Computer、学好English、学会做人――做个素质、有良心的人!


       本篇讲述查找相关的内容!这里的查找和ListSetMap他们具体实现的查找是有差别的,一般还是建议使用对象自身的查找!    

    八、查找

    8.1使用对象默认的比较器查找

             //比较器的原理在上面两篇已经讲过了!这里不重叙了!

             //List中查找指定对象的位置

        public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {

            if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)

                                                  //个数小于5000,或者为随机存取的List使用“二分发”查找

                                                   //看看我前面的FailFast机制的文章查查list有几种类型,有差别吗???

                return Collections.indexedBinarySearch(list, key);                                //见下面方法

            else            //否则使用遍历器查找,效率较低,原因见下面

                return Collections.iteratorBinarySearch(list, key);

        }

     

             //经典的二分发查找,注意查找前必须已经排序完成。

        private static <T> int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {

             int low = 0;                                                                                                               //低位索引,最小

             int high = list.size()-1;                                                                                  //高位索引,最大

             while (low <= high) {

                 int mid = (low + high) >> 1;                                                                         //两者位置的中间

                 Comparable<? super T> midVal = list.get(mid);                                    //得到中间位置的值

                 int cmp = midVal.compareTo(key);                     //将要查找的对象与中间值比较

                 if (cmp < 0)

                       low = mid + 1;                                                 //要查找的值小于中间值,在下半段比较

                 else if (cmp > 0)

                       high = mid - 1;                                                 //要查找的值小于中间值,在上半段比较

                 else

                       return mid; // key found                                  //相等就证明已经找到,返回位置

             }

             return -(low + 1);  // key not found                                //不存在,则返回应该所在位置的相反数

        }

     

     

    private static <T> int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key) {

                                                                                                           //根据自定义的比较器查找指定的元素

         int low = 0;

         int high = list.size()-1;

            ListIterator<? extends Comparable<? super T>> i = list.listIterator();

            while (low <= high) {

                int mid = (low + high) >> 1;                                             //移位完成除以2运算

                Comparable<? super T> midVal = get(i, mid);  //List中得到中间位的值,见后面

                int cmp = midVal.compareTo(key);                     //中间位与要查找值的比较

                if (cmp < 0)                                                               //分析同上

                    low = mid + 1;

                else if (cmp > 0)

                    high = mid - 1;

                else

                    return mid; // key found

            }

            return -(low + 1);  // key not found

        }

     

             //该方法查找中间位置的值效率较低。

    //主要是由于:总是通过从当前位置移动游标到中间位置。

        private static <T> T get(ListIterator<? extends T> i, int index) {

             T obj = null;

            int pos = i.nextIndex();                         //当前游标的位置

            if (pos <= index) {                                  //当前位置在中间以下,则向后遍历

                do {

                    obj = i.next();

                } while (pos++ < index);

            } else {                                                     //当前位置在中间以上,则向前遍历

                do {

                    obj = i.previous();

                } while (--pos > index);

            }

            return obj;

        }

     

     

    8.2使用自定义的比较器查找

           //这种查找方法可能对一般的朋友比较陌生

        public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {

            if (c==null)         return binarySearch((List) list, key);

            if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)

                return Collections.indexedBinarySearch(list, key, c);

            else

                return Collections.iteratorBinarySearch(list, key, c);

        }

     

        private static <T> int indexedBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {

             int low = 0;

             int high = l.size()-1;

             while (low <= high) {

                 int mid = (low + high) >> 1;

                 T midVal = l.get(mid);

                 int cmp = c.compare(midVal, key);                                         //整个比较方法就这个地方有差别

                 if (cmp < 0)

                       low = mid + 1;

                 else if (cmp > 0)

                       high = mid - 1;

                 else

                       return mid; // key found

             }

             return -(low + 1);  // key not found

        }

     

        private static <T> int iteratorBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {

             int low = 0;

             int high = l.size()-1;

            ListIterator<? extends T> i = l.listIterator();

            while (low <= high) {

                int mid = (low + high) >> 1;

                T midVal = get(i, mid);

                int cmp = c.compare(midVal, key);                                         //同上,唯一的差别

                if (cmp < 0)

                    low = mid + 1;

                else if (cmp > 0)

                    high = mid - 1;

                else

                    return mid;                                                                        // 找到对于的值,则返回该值

            }

            return -(low + 1);                                                                                //没有则返回该值应该位置的相反数

        }

     

    九、位置查找

           //在整个Java Collections Framework中只有List之类的对象对外界是暴露位置的,

           //位置实际上就是索引,对应于数组的下标!!!

           //不过我们下面这个位置查找不同!

    9.1第一次出现的位置

           //该方法查找某个List在另外一个List中的位置

        public static int indexOfSubList(List<?> source, List<?> target) {

            int sourceSize = source.size();

            int targetSize = target.size();

            int maxCandidate = sourceSize - targetSize;                       //最多查找差值的次数!!!

            if (sourceSize < INDEXOFSUBLIST_THRESHOLD ||

                (source instanceof RandomAccess&&target instanceof RandomAccess)) {

            nextCand:

                for (int candidate = 0; candidate <= maxCandidate; candidate++) {                    //查找次数循环

                    for (int i=0, j=candidate; i<targetSize; i++, j++)                     //每个对于位置元素是否相等

                        if (!eq(target.get(i), source.get(j)))

                            continue nextCand;  // Element mismatch, try next cand

                    return candidate;  // All elements of candidate matched target

                }

            } else {  // Iterator version of above algorithm

                ListIterator<?> si = source.listIterator();

            nextCand:

                for (int candidate = 0; candidate <= maxCandidate; candidate++) {

                    ListIterator<?> ti = target.listIterator();

                    for (int i=0; i<targetSize; i++) {

                        if (!eq(ti.next(), si.next())) {

                            // Back up source iterator to next candidate

                            for (int j=0; j<i; j++)

                                si.previous();

                            continue nextCand;

                        }

                    }

                    return candidate;

                }

            }

            return -1;  // No candidate matched the target

        }

     

    9.2最后的位置

           //和上面的方法基本相同,只是从后向前遍列而已

        public static int lastIndexOfSubList(List<?> source, List<?> target) {

            int sourceSize = source.size();

            int targetSize = target.size();

            int maxCandidate = sourceSize - targetSize;

     

            if (sourceSize < INDEXOFSUBLIST_THRESHOLD ||

                source instanceof RandomAccess) {   // Index access version

            nextCand:

                for (int candidate = maxCandidate; candidate >= 0; candidate--) {

                    for (int i=0, j=candidate; i<targetSize; i++, j++)

                        if (!eq(target.get(i), source.get(j)))

                            continue nextCand;  // Element mismatch, try next cand

                    return candidate;  // All elements of candidate matched target

                }

            } else {  // Iterator version of above algorithm

                if (maxCandidate < 0)

                    return -1;

                ListIterator<?> si = source.listIterator(maxCandidate);

            nextCand:

                for (int candidate = maxCandidate; candidate >= 0; candidate--) {

                    ListIterator<?> ti = target.listIterator();

                    for (int i=0; i<targetSize; i++) {

                        if (!eq(ti.next(), si.next())) {

                            if (candidate != 0) {

                                // Back up source iterator to next candidate

                                for (int j=0; j<=i+1; j++)

                                    si.previous();

                            }

                            continue nextCand;

                        }

                    }

                    return candidate;

                }

            }

            return -1;  // No candidate matched the target

        }

     

    十、频繁

             //查找某个对象在Collection中出现的次数,极其简单的方法

        public static int frequency(Collection<?> c, Object o) {

            int result = 0;

            if (o == null) {

                for (Object e : c)

                    if (e == null)     result++;

            } else {

                for (Object e : c)

                    if (o.equals(e))     result++;

            }

            return result;

        }

     

     

    十一、查找取代

           //查找某个对象,用其它对象取代他,所有的这种对象对会被取代

        public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) {

            boolean result = false;

            int size = list.size();

            if (size < REPLACEALL_THRESHOLD || list instanceof RandomAccess) {

                if (oldVal==null) {

                    for (int i=0; i<size; i++) {

                        if (list.get(i)==null) {

                            list.set(i, newVal);

                            result = true;

                        }

                    }

                } else {

                    for (int i=0; i<size; i++) {

                        if (oldVal.equals(list.get(i))) {

                            list.set(i, newVal);

                            result = true;

                        }

                    }

                }

            } else {

                ListIterator<T> itr=list.listIterator();

                if (oldVal==null) {

                    for (int i=0; i<size; i++) {

                        if (itr.next()==null) {

                            itr.set(newVal);

                            result = true;

                        }

                    }

                } else {

                    for (int i=0; i<size; i++) {

                        if (oldVal.equals(itr.next())) {

                            itr.set(newVal);

                            result = true;

                        }

                    }

                }

            }

            return result;

        }

     

           又一篇结束了!好像快了点!不过这个系列的文章我酝酿好久了!有收获吧!这个系列一共10篇左右!下一个系列是IO至少10篇!我加油写,你加油看哦!谢谢支持!


  • 相关阅读:
    could not load file or assembly "System.Web.Mvc...
    .Net利用cwbx.dll call AS400 program得到数据
    fastadmin 如何构建组合题--Cannot read property '0' of undefined
    fastadmin的前端js文件中api和event的区别,formatter的意思
    fastadmin是如何使用art-template的,以及如何在js模板中,嵌套JS模板
    学习fastadmin的新技巧:去git里面看文件的修改
    thinphp5,模型调用模型,和控制器调用模型的区别
    fa使用技巧+tp5技巧总结
    input autocomplete的作用是什么?
    fastadmin 实现标签的多选研究---基于fa的test案例,已经CMS中的标签写法
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959550.html
Copyright © 2020-2023  润新知