《改善java代码》第五章:数组与集合代码优化

码云地址:https://gitee.com/yaohuiqin/codeimprove/tree/master/src/per/yhq/objectimprove/listorarrayprove


  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;
        }
    }

    输出的值是:

  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是一个稳定类,为了排序而修改它显然不是好办法,而实现

    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));  需要循环遍历,源代码:

    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是如何存储元素的,首先要说明以下三点:1、table数组的长度永远是2的n次幂。2、table数组中的元素是Entry类型。3、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. 总结:
    1、List: 实现了List接口的集合主要有:ArrayList、LinkedList、Vector、Stack,其中ArrayList是一个动态数组,LinkedList是一个双向链表,Vector是一个线程安全的动态数组,Stack是一个对象栈,遵循先进后出的原则。

    2、Set是不包含重复元素的集合,其主要的实现类有:EnumSet、HashSet、TreeSet。其中EnumSet是枚举类型的专用Set,所有元素都是枚举类型;HashSet是以哈希码决定其元素位置的Set,其原理与HashMap相似,它提供快速的插入和查找方法;  TreeSet是一个自动排序的Set,它实现了SortedSet接口。

    3、Map可以分为排序Map和非排序Map。排序Map主要是TreeMap类,它根据Key值进行排序。非排序Map主要包括:HashMap、HashTable、Properties、EnumMap等,其中Properties是HashTable的子类。它的主要用途是从Property文件中加载数据,并提供方便的读写操作。EnumMap则要求其Key必须是某一个枚举类型。

    4、Queue,队列,分为两类,一类是阻塞式队列,队列满了以后再插入元素则会抛出异常。主要包括:ArrayBlockingQueue、PriorityBlockingQueue、LinkedBlockingQueue。其中ArrayBlockingQueue是一个以数组方式实现的有界阻塞队列。PriorityBlockingQueue是依照优先级组建的队列,LinkedBlockingQueue是通过链表实现的阻塞队列。         另一类是非阻塞队列,无边界的,只要内存允许,都可以持续追加元素。我们经常使用的是PriorityQueue类。

    5、数组:数组与集合最大的区别就是数组能够容纳基本类型。 所有的集合底层存储都是数组。

    6、工具类:数组的工具类是java.util. Arrays 和 java.lang.reflect.Array   集合的工具类:java.util.Collections

posted @ 2018-07-20 13:36  yaohuiqin  阅读(162)  评论(0编辑  收藏  举报