Java SE学习总结 Day (18)

Day 18开篇:
      
        "
今天java基础主要学习了ArrayList底层实现的成员属性,构造方法,成员方法,LinkedList的概念,特有成员方法, (存储方式的先进的后出,队列(先进的先出)等。 "



知识点反馈:

今天的知识点总结的思维导图

 

 

一.ArrayList底层实现
1. 定义:ArrayList是List的可变数组的实现方式
 
2. JDK7和JDK8:
(1)JDK7:类似饿汉式、在创建对象,使用无参构造的时候,其实就创建了一个长度为10的一个数组进行维护
(2)JDK8:类似懒汉式、在创建对象,使用无参构造的时候,并没有给数组指定任何的长度,而是在第一个元素添加的时候才会去生成一个10个长度的数组
 
3. 成员属性:
(1)//默认的容量大小
         private static final int DEFAULT_CAPACITY = 10;    
 
(2)//默认的空参数组常量(跟元素相关)
         private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
(3)//重要,这个数组其实就是整个集合的元素的维护数组,
                transient Object[] elementData;
 
(4)//数组的元素个数
                private int size;
 
(5) private static final Object[] EMPTY_ELEMENTDATA = {};
 
4. 构造方法:
(1)概念:从这个构造,我们可以明白,默认情况他的大小为0的空数组,但是如果我们有进行构造参数传递的时候,则会新创建一个对象,这个对象的大小则我们传递的空间大小
(2)例子:
                public ArrayList(int initialCapacity) {//10
                if (initialCapacity > 0) {
                    this.elementData = new Object[initialCapacity];
                } else if (initialCapacity == 0) {
                    this.elementData = EMPTY_ELEMENTDATA;
                } else {
                    throw new IllegalArgumentException("Illegal Capacity: "+
                                                       initialCapacity);
                }
            }        
(3)无参构造:我们在没有任何值传递的时候,他的默认大小就是一个空数组,为0。但是不会有新对象产生
(4)例子:
                public ArrayList() {
                this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
             }         
(5)这个构造就是传一个Collection的具体实现类对象,那么也就意味着这个构造实现的空间大小取决于你传入对象转换为数组之后的具体大小
(6)例子:
            public ArrayList(Collection  c) {
                elementData = c.toArray();
                if ((size = elementData.length) != 0) {
                    // c.toArray might (incorrectly) not return Object[] (see 6260652)
                    if (elementData.getClass() != Object[].class)//反射/序列化
                        elementData = Arrays.copyOf(elementData, size, Object[].class);
                } else {
                    // replace with empty array.
                    this.elementData = EMPTY_ELEMENTDATA;
                }
            }
 
5. 成员方法:
(1)get获取:
.get(0); 
                       public Object get(int index) {//0
                                rangeCheck(index);
                                return elementData(index);//toobug
                           }
 
                            private void rangeCheck(int index) {//0
                                if (index >= size)
                                    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
                            }
                            Object elementData(int index) {//0
                                return  elementData[0];  
                            }
解释:因为ArrayList是采用数组的结构进行存储的,所有他的get使用的非常简单,先判断你传递的数据有没有超出转换数组后的长度,如果超出则产生数组越界异常,如果在长度范围之类则直接调用elementData这个方法对数组进行查询 。
(2)add添加:
                         public boolean add(E e) {
                                ensureCapacityInternal(size + 1); //11 // Increments modCount!!
                                elementData[size++] = e;//toobug 
                                return true;
                         }
                         private void ensureCapacityInternal(int minCapacity) {
                        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
                     }  
 
                     private void ensureExplicitCapacity(int minCapacity) {
                        modCount++;
 
                        // overflow-conscious code
                        if (minCapacity - elementData.length > 0)
                            grow(minCapacity);
                    } 
 
                    private void grow(int minCapacity) {
                        // overflow-conscious code
                        int oldCapacity = elementData.length;
                        int newCapacity = oldCapacity + (oldCapacity >> 1);
                        if (newCapacity - minCapacity < 0)
                            newCapacity = minCapacity;
                        if (newCapacity - MAX_ARRAY_SIZE > 0)
                            newCapacity = hugeCapacity(minCapacity);
                        // minCapacity is usually close to size, so this is a win:
                        elementData = Arrays.copyOf(elementData, newCapacity);
                    }
解释: ArrayList的add实现也比较简单,插入元素之前会先检查你的这个集合是否需要扩容(第一次add肯定扩容10个),每一次添加的元素位置为size++(每一次递增1个,其实就是每一个存在元素的后面)。如果容量已经超出了原来是10个则继续调用grow进行再次扩容,扩容的机制也比较简单,就是两个数组进行不断的拷贝替换,新数组自动增加10个元素位置。
(3)set插入:
                            public E set(int index,Object element) {
                        rangeCheck(index);//判断是否越界
                        Object oldValue = elementData(index);//确定数组位置,并且将元素提出
                        elementData[index] = element;
                        return oldValue;
                    }
                    
解释:set方法其实跟get基本一致
(4)remove方法:
                     public E remove(int index) {
                        rangeCheck(index);//判断是否越界
                        modCount++;//快速失败机制的计数器(暂时你们别管)
                        E oldValue = elementData(index);
                        int numMoved = size - index - 1;
                        if (numMoved > 0)
                            System.arraycopy(elementData, index+1, elementData, index,
                                             numMoved);
                        elementData[--size] = null; // clear to let GC do its work
 
                        return oldValue;
                    }
                    
解释:所谓的删除,其实就之前先判断是不是越界,其次找到你需要删除的位置(进行一定的的位置偏移来校正位置),然后通过数组拷贝进行重重新赋值。
(5) 在测试存储元素的时候,每一次添加元素,第一件事是先检查长度,如果长度不够则进行扩展。其实java为了让我们更加合理的去管理我们数组扩容的问题,提供了一个公共方法ensureCapacity来进行容量的手动管理,可以减少集合数组分配容量的次数
例子:
                        temp.contains(book)
 
                            public boolean contains(Object o) { //Object o = new Book();
                        return indexOf(o) >= 0;
                     }
 
                     public int indexOf(Object o) {//Object o = new Book();
                        if (o == null) {//
                            for (int i = 0; i < size; i++)
                                if (elementData==null)
                                    return i;
                        } else {
                            for (int i = 0; i < size; i++)
                                if (o.equals(elementData))//o.equals()
                                    return i;
                        }
                        return -1;
                    }
 
6.  void        ensureCapacity(int minCapacity):调整集合维护数组的默认的容量大小
      void        trimToSize():将集合的维护数组长度,调整为实际元素个数值一致
 
7. 需求:
创建了一个集合,里面有很多元素,我想判断这个集合当中是否存在一个叫做toobug的元素值,如果不存在,则添加一个pony的元素值
(1) ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常
(2) ArrayList本身其实也是采用快速失败机制进行处理,通过一个modCount这个参数来进行实现,Iterator本身和ArrayList是没有直接关联的。 但是有一种情况可能会出现并发现象(同一个时间点同时执行了某一件事),当iterator已经在hasNext的时候就已经判断了不存在下一个元素,而ArrayList.add()又恰好在这同一时刻又新添加了元素,所以可能造成数据错乱,JDK本身就考虑好了这件事,他使用了一个计数器modCount 迭代器的执行和add()的执行其实都有共同去维护一个计数的过程,只要在计数过程当中,计数被修改了即迭代器立马失败,而不是冒着危险在将来 一个不确定的时候再去产生异常。
(3) 解决方式:
<1>使用迭代器的添加跟删除
<2>不使用迭代器
 
8. 例子:
(1)当一个对象需要去重时,我们采用方式和字符串去重是一模一样的,但是结果不理想原因呢?
答:字符串存储的值是一个具体的值,可以进行内容直接对比;而对象在集合当中存储的是一个对象的具体地址值(而每一个对象的地址值肯定不同,所以无法判重)
(2)Object o = new Book();
我们现在使用的是contains()方法,其实contains底层维护的还是一个equals,而这个equals是Object的,所以判断是地址值是否相等那么现在我们需要执行自己自定义的equals,根据多态的执行特性,我们重写子类就可以了。
 
源码:
             temp.contains(book)
 
                            public boolean contains(Object o) { //Object o = new Book();
                        return indexOf(o) >= 0;
                     }
 
                     public int indexOf(Object o) {//Object o = new Book();
                        if (o == null) {//
                            for (int i = 0; i < size; i++)
                                if (elementData==null)
                                    return i;
                        } else {
                            for (int i = 0; i < size; i++)
                                if (o.equals(elementData))//o.equals()
                                    return i;
                        }
                        return -1;
                    }
 
 
二.LinkedList
1. 概念:
是List子类当中,采用双向链表的一种实现方式的子类,内部不存在任何数组的声明,而是定义了链表的开端和结尾
 
2. 特有成员方法:
(1)void addFirst(E e):将元素添加到集合首位
(2)void addLast(E e)  将元素添加到集合结尾
(3)E getFirst() 获取第一个元素
(4)E getLast(E e)  获取最后一个元素
(5)E removeFirst() 删除第一个元素
(6)E removeLast() 删除最后一个元素
 
3.栈(存储方式的先进的后出):
(1)void push(E e):依照栈结构的方式进行数据插入(压栈)
(2)E        pop()  依照栈结构的方式将数据取出(弹栈)
 
4.队列(先进的先出):
(1)boolean        offer(E e): 依照队列结构的方式将数据进行插入(因为对列是需要进行排队是,所有永远最后一个)
(2)E poll() 依照队列的结构取出首位数据,并且删除
 
5.ArrayList/LinkedList/Vector的区别:
(1)ArrayList/LinkedList本质上两者都是线程不安全的,相比线程安全的Vector来说,效率要比较快
(2)另外ArrayList底层维护的是一个动态数组的数据结构,而LinkedList基于一个双向链表的一个数据结构
(3)对于随机访问get/set,ArrayList绝对优于LinkedList,因为LinkedList要不断的移动指针
(4)对于新增(add)和删除(remove)数据来说,LinkedList比较有优势,因为ArrayList不断创建新的集合进行拷贝和运算
(5)Vector和ArrayList几乎一致,唯一的区别就是Vector是一个强同步类(synchronized) ,内存开销特别大,访问特别慢
(6)正常情况下ArrayList是完全可以取代Vector,因为我们自己可以手动控制任何对象是否同步
 
6.平时该怎么选择List的子类?
(1)要安全 Vector(即使这玩意安全,我们也不用)
            查询多
            增删多
(2)要效率:
            查询多
                ArrayList
            增删多
                LinkedList
 
(3)如果你懒得想,那就直接使用ArrayList
 
7. 例子:
public class Demo01 {
    public static void main(String[] args) {
        Vector v = new Vector();
        LinkedList list = new LinkedList();
        list.add("toobug");
        list.add("王健林");
        list.add("马化腾");
 
        //void addFirst(E e):将元素添加到集合首位
        list.addFirst("二狗子");
        //void addLast(E e)  将元素添加到集合结尾:
        list.addLast("三狗子");
 
 
        list.push("文子");//根据栈结构的方式插入数据(默认第一位,因为下面有数据,只能压下去)
        System.out.println("出栈:"+list.pop());//根据栈结构的方式取出数据,现在出栈了,原来在集合数据也删除了
 
        list.offer("王思聪");
        list.offer("刘同");
        list.offer("金浩森");
 
        System.out.println(list.poll());
        System.out.println(list);
    }
}
 
发布了29 篇原创文章 · 获赞 7 · 访问量 3164

猜你喜欢

转载自blog.csdn.net/weixin_45406656/article/details/104255079
今日推荐