美文网首页Java语言
改善 Java 程序的151个建议之数组和集合(一)

改善 Java 程序的151个建议之数组和集合(一)

作者: AaronSimon | 来源:发表于2019-04-08 21:47 被阅读7次

    1. 警惕数组的浅拷贝

       public static void main(String[] arg){
            Balloon [] a = new Balloon[5];
            for(int i = 0;i < 5; i++){
                a[i] = new Balloon(i,Color.values()[i]);
                a[i].setId(i);
                a[i].setColor(Color.values()[i]);
            }
            for(int i = 0;i < 5; i++){
                System.out.println("a:"+a[i].getColor());
            }
            
            Balloon[] b = Arrays.copyOf(a,a.length);
            
            //修改b数组最后一个元素的属性
            b[4].setColor(Color.Black);
            for(int i = 0;i < 5; i++){
                System.out.println("a:"+a[i].getColor()+"--b:"+b[i].getColor());
            }
        }
        
        enum Color{
            Red,Blue,Yellow,Black,White;
        }
        
        static class Balloon{
            private int id;
            private Color color;
    
            public Balloon(int id, Color color) {
                this.id = id;
                this.color = color;
            }
    
            public int getId() {
                return id;
            }
    
            public void setId(int id) {
                this.id = id;
            }
    
            public Color getColor() {
                return color;
            }
    
            public void setColor(Color color) {
                this.color = color;
            }
        }
    
    
    a:Red
    a:Blue
    a:Yellow
    a:Black
    a:White
    a:Red--b:Red
    a:Blue--b:Blue
    a:Yellow--b:Yellow
    a:Black--b:Black
    a:Black--b:Black
    

    为什么修改了b数组最后一个元素的颜色属性,a数组的最后一个元素颜色属性也发生了变化?原因如下:
    通过copyOf方法产生的数组是一个浅拷贝,这与序列的浅拷贝完全相同,基本类型拷贝值,其它都是拷贝引用地址。

    2. 避开基本类型数组转换列表陷阱

    看下面一段代码:

        int[] data = {1,2,3,4,5,6};
        List<int[]> ints = Arrays.asList(data);
        System.out.println(ints.size());
    

    ints的长度为什么变成了1,而不是6呢?查看asList的源码你会发现:

        public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }
    

    asList的输入参数是一个泛型变长参数,而基本类型是不能泛型化的,除非使用对应的包装类型,那为什么传递int型数组编译没有报错呢?
    在Java中,数组是一个对象,它是可以被泛型化的,上面的代码中把int数组作为了T的类型,因此转化后list中只有一个int数组的元素。修改后代码如下:

        Integer[] data = {1,2,3,4,5,6};
        List<Integer> integers = Arrays.asList(data);
        System.out.println(integers.size());
    

    3. asList产生的对象不可更改

        Integer[] data = {1,2,3,4,5,6};
        List<Integer> integers = Arrays.asList(data);
        integers.add(9);
        System.out.println(integers.size());
    

    结果:

    Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.AbstractList.add(AbstractList.java:148)
        at java.util.AbstractList.add(AbstractList.java:108)
        at com.hummer.personal.mdm.MdmController.main(MdmController.java:133)
    

    为什么在使用add方法时会抛出异常呢?

        public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }
    

    查看源码发现asList返回的ArrayList类是Arrays工具类内置类

     private static class ArrayList<E> extends AbstractList<E>
            implements RandomAccess, java.io.Serializable
        {
            private static final long serialVersionUID = -2764017481108945198L;
            private final E[] a;
    
            ArrayList(E[] array) {
                a = Objects.requireNonNull(array);
            }
    
            @Override
            public int size() {
                return a.length;
            }
    
            @Override
            public Object[] toArray() {
                return a.clone();
            }
    
            @Override
            @SuppressWarnings("unchecked")
            public <T> T[] toArray(T[] a) {
                int size = size();
                if (a.length < size)
                    return Arrays.copyOf(this.a, size,
                                         (Class<? extends T[]>) a.getClass());
                System.arraycopy(this.a, 0, a, 0, size);
                if (a.length > size)
                    a[size] = null;
                return a;
            }
    
            @Override
            public E get(int index) {
                return a[index];
            }
    
            @Override
            public E set(int index, E element) {
                E oldValue = a[index];
                a[index] = element;
                return oldValue;
            }
    
            @Override
            public int indexOf(Object o) {
                E[] a = this.a;
                if (o == null) {
                    for (int i = 0; i < a.length; i++)
                        if (a[i] == null)
                            return i;
                } else {
                    for (int i = 0; i < a.length; i++)
                        if (o.equals(a[i]))
                            return i;
                }
                return -1;
            }
    
            @Override
            public boolean contains(Object o) {
                return indexOf(o) != -1;
            }
    
            @Override
            public Spliterator<E> spliterator() {
                return Spliterators.spliterator(a, Spliterator.ORDERED);
            }
    
            @Override
            public void forEach(Consumer<? super E> action) {
                Objects.requireNonNull(action);
                for (E e : a) {
                    action.accept(e);
                }
            }
    
            @Override
            public void replaceAll(UnaryOperator<E> operator) {
                Objects.requireNonNull(operator);
                E[] a = this.a;
                for (int i = 0; i < a.length; i++) {
                    a[i] = operator.apply(a[i]);
                }
            }
    
            @Override
            public void sort(Comparator<? super E> c) {
                Arrays.sort(a, c);
            }
        }
    

    而这个内置类中并没有实现add方法,其add方法在父类AbstractList中

        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
    

    因此会抛出异常

    4. 频繁插入和删除使用LinkedList

    • ArrayList是动态扩展的数组,插入和删除元素时需要移动后面元素
    • LinkedList是双向链表的数据结构,插入和删除元素只是前后元素引用指针的变化
    • 修改元素ArrayList比LinkedList快,因为LinkedList修改用了entry方法定位元素,而arraylist的修改则是数组元素的直接替换
    • 增加元素两者效率基本相同

    5. 列表相等只需关心元素数据

    ArrayList<String> str = new ArrayList();
    str.add("aa");
    
    Vector<String> str2 = new Vector();
    str2.add("aa");
    
    System.out.println(str.equals(str2));//true
    

    两者都实现了List接口,也都继承了AbastractList抽象类,其equals方法在AbastractList中定义,equals方法不关心List的具体实现类,只要所有元素相等,并且长度也相等就表明两个List相等。其他的集合类型,如Set,Map等与此相同。

    6. 子列表只是原列表的一个视图

      public static void main(String[] arg) {
        List<String> c = new ArrayList<>();
        c.add("A");
        c.add("B");
        c.add("C");
    
        ArrayList<String> c1 = new ArrayList<>(c);
    
        List<String> c2 = c.subList(0, c.size());
        c2.add("D");
    
        System.out.println("c==c1 ? "+ c.equals(c1));//false
        System.out.println("c==c2 ? "+ c.equals(c2));//true
      }
    
    • c1是通过ArrayList的构造函数创建的,它是通过数组的copyOf动作生成的,所生成的c1与原列表c之间没有任何关系(虽然是浅拷贝,但是元素类型是String,也就是说元素是深拷贝)
    • subList操作是在原始列表上的操作,它自身并没有生成数组或是链表,也就是子列表只是原列表的一个视图(View),所以修改动作都反映在原列表上

    相关文章

      网友评论

        本文标题:改善 Java 程序的151个建议之数组和集合(一)

        本文链接:https://www.haomeiwen.com/subject/oqsmiqtx.html