《改善java代碼》第五章:數組與集合代碼優化

碼雲地址:https://gitee.com/yaohuiqin/codeimprove/tree/master/src/per/yhq/objectimprove/listorarrayprovejava


  1. 性能方面優先考慮數組:
    對基本類型進行求和運算時,數組的效率是集合的10倍。

  2. 對數組擴容方法:
    public class expandCapacity {
        public static void main(String[] args) {
            String arrays[] = new String[10];
            System.out.println(arrays.length);
            arrays = expandCapacity(arrays,13);
            System.out.println(arrays.length);
        }
        public static  <T> T[] expandCapacity(T[] datas,int newlen){
            newlen = newlen < 0 ? 0 : newlen;
            return Arrays.copyOf(datas,newlen);
        }
    }
  3. 警戒數組的淺拷貝:
    public class shallowCopy {
        public static void main(String[] args) {
            int balloonNum=7;
            Balloon[] box = new Balloon[balloonNum];
            for (int i=0;i<balloonNum;i++){
                box[i]=new Balloon(Color.values()[i],i);
            }
            Balloon[] copybox = Arrays.copyOf(box,box.length);
            copybox[6].setColor(Color.blue);
           //打印:
            for (Balloon ballon:box){
                System.out.print(ballon.getColor()+"id:"+ballon.getId()+"  ");
            }
            System.out.println();
            System.out.println("copybox數組的值:");
            for (Balloon ballon:copybox){
                System.out.print(ballon.getColor()+"id:"+ballon.getId()+"  ");
            }
        }
        public static class Balloon{
            Color color;
            int id;
            @Override
            public String toString() {
                return "Balloon{" +
                        "color='" + color + '\'' +
                        ", id=" + id +
                        '}';
            }
            public Balloon(Color color, int id) {
                this.color = color;
                this.id = id;
            }
            public Color getColor() {
                return color;
            }
            public void setColor(Color color) {
                this.color = color;
            }
            public int getId() {
                return id;
            }
            public void setId(int id) {
                this.id = id;
            }
        }
        public static enum Color{
            Red,Orange,yellow,green,indigo,blue,violet;
        }
    }

    輸出的值是:node

  4. 在明確場景下爲集合指定初始化容量:
    集合在add操做時,會自動給集合擴容,例如,ArrayList就按1.5倍擴容,每次擴容會執行
    Arrays.copyOf(elementData, newCapacity),影響性能。

  5. 獲取次大於最大值的元素:
    注意,最大值多是多個,因此排序後取次大值的元素的方式不可取。首先須要去重複而後再排序,去重不須要本身寫,數組不能自動去重,可是Set集合卻能夠,並且Set的子類TreeSet還能自動排序。
    public static int getSecond(Integer[] data){
            List<Integer> dataList = Arrays.asList(data);
            TreeSet<Integer> ts = new TreeSet<Integer>(dataList);
            return  ts.lower(ts.last());
        }
  6. 注意,原始數據類型不能做爲aslist參數,不然會引發程序混亂。  
    asList轉換後的arraylist  是Array類下的子類ArrayList,不能進行add操做,會報錯:
    Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.AbstractList.add(AbstractList.java:148)
        at java.util.AbstractList.add(AbstractList.java:108)
        at per.yhq.objectimprove.listorarrayprove.GetSecondMax.main(GetSecondMax.java:21)
  7. 對數據排序時,推薦使用Comparator 進行排序:
      在java中,想要給數據排序,有兩種實現方式,一種是實現Comparable接口,一種是實現Comparator接口。
      先看一個例子:給公司職員排序時的代碼,先按員工工號排序,代碼以下:
    public class Compareobjectoflist {
        public static void main(String[] args) {
            List<Employee> list = new ArrayList<Employee>(5);
            list.add(new Employee(1001,"張三",Position.Boss));
            list.add(new Employee(1342,"李四",Position.Manage));
            list.add(new Employee(990,"姚慧芹",Position.Staff));
            list.add(new Employee(1003,"位喜會",Position.Boss));
            list.add(new Employee(890,"張三",Position.Staff));
            Collections.sort(list);
            //改善代碼,利用實現了接口的Comparator類重寫compare方法,改變排序規則,無需改變Employee類的代碼
            //Collections.sort(list,new PositionComparator());
            for (Employee e:list){
                System.out.println(e);
            }
        }
    
        private static class Employee implements Comparable<Employee>{
            private int id;
            private String name;
            private Position position;
    
            public Employee(int id, String name, Position position) {
                this.id = id;
                this.name = name;
                this.position = position;
            }
            @Override
            public int compareTo(Employee o) {
                return new CompareToBuilder().append(id,o.id).toComparison();
            }
    
            @Override
            public String toString() {
                return ToStringBuilder.reflectionToString(this);
            }
    
            public int getId() {
                return id;
            }
            public void setId(int id) {
                this.id = id;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public Position getPosition() {
                return position;
            }
            public void setPosition(Position position) {
                this.position = position;
            }
        }
        private enum Position{
            Boss,Manage,Staff
        }
    
    //    static class PositionComparator implements Comparator<Employee>{
    //
    //        @Override
    //        public int compare(Employee o1, Employee o2) {
    //            return o1.getPosition().compareTo(o2.position);
    //        }
    //    }
    }
    

      上面的代碼只是根據員工的id進行排序,但咱們想要按照職位來排序時,再改Employee時已不合適了,Employee是一個穩定類,爲了排序而修改它顯然不是好辦法,而實現git

    Comparator接口再重寫compare()方法時,改Collections.sort(list)  爲  Collections.sort(list,new PositionComparator())時,能夠更好的修改排序方式。
      若是想修改爲先根據員工的職位排序,再根據員工id排序時,能夠用apache的工具類簡化處理:
    //改爲職工排序和根據工號排序
        static class PositionComparator2 implements Comparator<Employee>{
            @Override
            public int compare(Employee o1, Employee o2) {
                return new CompareToBuilder().append(o1.position,o2.position).append(o1.id,o2.id).toComparison();
            }
        }
    
  8. 實現了compareTo方法,就應該覆寫equals方法,確保二者同步。
    indexOf依賴equals方法查找,binarySearch則依賴compareTo方法查找
    equals是判斷元素是否相等,compareTo是判斷元素在排序中的位置是否相同。

  9. 推薦用兩個集合的運算:交集,並集,差集。
    若是用對兩個集合進行遍從來計算,並不優雅。
    交集:list.retainAll(list2);
    並集:list.addAll(list2);
    差集:list.removeAll(list2);
    無重複的並集 : list.removeAll(list2); list.addAll(list2)

  10. 利用Collections.shuffle(lists);  方法打亂lists的排序,能夠用來抽獎,也能夠用在安全傳輸方面。

  11. 查詢集合類中是否包含某元素,推薦用hashMap而不是用list集合  ,注意,HashMap中的HashCode應避免衝突。
    public class QuickFindElementByHashMap {
        public static void main(String[] args) {
            int size = 10000;
            List<String> list = new ArrayList<String>(size);
            for(int i =0;i<size;i++){
                list.add("value"+i);
            }
            long start = System.nanoTime();
            list.contains("value"+(size-1));
            System.out.println("list執行時間:" + (System.nanoTime()-start)+"ns");
            Map<String,String> map = new HashMap<String, String>(size);
            for (int i=0;i<size;i++){
                map.put("key"+i,"value"+i);
            }
            start = System.nanoTime();
            map.containsKey("value"+(size-1));
            System.out.println("hashmap執行時間:" + (System.nanoTime()-start)+"ns");
        }
    }

    執行結果:


    緣由分析:list.contains("value"+(size-1));  須要循環遍歷,源代碼:apache

    public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
    
    public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }

    而 map.containsKey("value"+(size-1));  無需循環遍歷源代碼:數組

    public boolean containsKey(Object key) {
        return getNode(hash(key), key) != null;
    }
    static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) {
                if (first.hash == hash && // always check first node
                    ((k = first.key) == key || (key != null && key.equals(k))))
                    return first;
                if ((e = first.next) != null) {
                    if (first instanceof TreeNode)
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }
    

      解析: tab[(n - 1) & hash] 根據hash值和table的長度獲取數組的下標, 先講解HashMap中的table是如何存儲元素的,首先要說明如下三點:一、table數組的長度永遠是2的n次冪。二、table數組中的元素是Entry類型。三、table數組中的元素位置是不連續的。

    安全

  12. Vector是ArrayList的多線程版本,HashTable是HashMap的多線程版本

  13. Set 的元素不可重複,List的元素是能夠重複的,這裏的重複是equals方法的返回值相等。
    Set有一個經常使用的類:TreeSet。改類實現了默認排序爲升序的Set集合。若是插入一個元素,默認會按照升序排列。可是這僅限於集合加入元素時對其排序,可是並不試用於可變量的排序。
    解決方式:  修改元素後:Set = new TreeSet<Person> (new ArrayList<Person> (Set))   不可用方法Set = new TreeSet(set),形成淺拷貝。或者不用treeset,改用List

  14. 總結:
    一、List: 實現了List接口的集合主要有:ArrayList、LinkedList、Vector、Stack,其中ArrayList是一個動態數組,LinkedList是一個雙向鏈表,Vector是一個線程安全的動態數組,Stack是一個對象棧,遵循先進後出的原則。

    二、Set是不包含重複元素的集合,其主要的實現類有:EnumSet、HashSet、TreeSet。其中EnumSet是枚舉類型的專用Set,全部元素都是枚舉類型;HashSet是以哈希碼決定其元素位置的Set,其原理與HashMap類似,它提供快速的插入和查找方法;  TreeSet是一個自動排序的Set,它實現了SortedSet接口。

    三、Map能夠分爲排序Map和非排序Map。排序Map主要是TreeMap類,它根據Key值進行排序。非排序Map主要包括:HashMap、HashTable、Properties、EnumMap等,其中Properties是HashTable的子類。它的主要用途是從Property文件中加載數據,並提供方便的讀寫操做。EnumMap則要求其Key必須是某一個枚舉類型。

    四、Queue,隊列,分爲兩類,一類是阻塞式隊列,隊列滿了之後再插入元素則會拋出異常。主要包括:ArrayBlockingQueue、PriorityBlockingQueue、LinkedBlockingQueue。其中ArrayBlockingQueue是一個以數組方式實現的有界阻塞隊列。PriorityBlockingQueue是依照優先級組建的隊列,LinkedBlockingQueue是經過鏈表實現的阻塞隊列。         另外一類是非阻塞隊列,無邊界的,只要內存容許,均可以持續追加元素。咱們常用的是PriorityQueue類。

    五、數組:數組與集合最大的區別就是數組可以容納基本類型。 全部的集合底層存儲都是數組。

    六、工具類:數組的工具類是java.util. Arrays 和 java.lang.reflect.Array   集合的工具類:java.util.Collections
    多線程

相關文章
相關標籤/搜索