Java面试准备之探究集合(未完)

摘要:之前虽然对集合框架一些知识点作了总结,但是想想面试可能会问源码,于是又大致研究了一下集合框架的一些实现类的源码,在此整理一下。

一.集合框架

二.深究实现类

1.ArrayList源码实现

  ArrayList内部维护了一个动态数组,如果没有显式的初始化的话,动态数组的默认容量是10,当数组容量已满时,每次将容量扩大至1.5倍加1。

  ArrayList的remove、add、clear等方法的实现原理都是对内部的Object数组进行操作,需要注意的是,在add方法执行前,都会提前对数组的容量进行确认,如果已满,则先进行扩容,此处很简单,就不对源码进行解析了。

  需要注意的是有一个方法trimToSize,这个方法的作用是去掉预留位置,在内存紧张时会用到。

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    /**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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;
    }

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

ArrayList

2.Vector源码实现

  Vector同ArrayList相同,也是内部维护了一个动态数组,数组默认长度是10,但是扩容方案与ArrayList有所不同,Vector扩容后的容量取决于扩容因子capacityIncremen和旧数组容量oldCapacity,法则如下:int newCapacity = (capacityIncrement > 0) ?(oldCapacity + capacityIncrement) : (oldCapacity * 2),注意:在计算出新数组尺寸后,还要与Vector类内部定义的最小值和最大值进行比较,如果超过上下限,那么新数组容量就等于上下限。另外,如果初始化时未传入扩容因子,那么扩容因子默认为0。

  Vector的基本增删等操作的实现原理与ArrayList相同,都是简单的对数组进行操作。

  Vector是线程安全的,实现的方式就是在基本操作的方法添加了synchronized关键字进行修饰,这样就确保了这个方法只能在同一时刻只能被一个线程访问,从而保证了多线程访问的安全性。

  Vector内部也实现了迭代器,不过是用Enumeration来实现的。

    public Enumeration<E> elements() {
        return new Enumeration<E>() {
            int count = 0;

            public boolean hasMoreElements() {
                return count < elementCount;
            }

            public E nextElement() {
                synchronized (Vector.this) {
                    if (count < elementCount) {
                        return elementData(count++);
                    }
                }
                throw new NoSuchElementException("Vector Enumeration");
            }
        };
    }

Vector

3.LinkedList

  LinkedList内部通过一个双向链表实现,每一个传入的对象都会转化为一个Node节点(Entry和Node都是通过一个内部类实现的),LinkedList的增删基本操作的实现就是通过对链表的节点地址赋值或置null来实现的,具体是link和unlink类似的方法。

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

LinkedList

4.HashSet

  HashSet内部是通过HashMap底层来实现的,只不过是将HashMap的value全部赋值为一个常量Object对象,内部的增删等方法都是直接调用hashMap的方法,故在此不做赘述,等HashMap再深入分析。

    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
     public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

HashSet

5.TreeSet

  TreeSet内部是通过TreeMap底层来实现的,与HashSet相同,都是将value设定成一个常量。

6.HashMap

  HashMap底层是通过数组加链表的数据结构实现的,为什么使用数据加链表的形式呢?这就引出了一个很重要的问题,就是哈希冲突的问题,我们都知道,对于hashXXX这种实现类,确保key唯一性的方法就是hashCode和equals方法,当往HashMap中存入元素时,会对key进行检测,判断是否已经存在相同的key,

时间: 2024-10-15 15:54:29

Java面试准备之探究集合(未完)的相关文章

阿里P7分享最新java面试——线程面试题集合

1.说说进程,线程,协程之间的区别 简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高.线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位.同一进程中的多个线程之间可以并发执行. 2.你了解守护线程吗?它和非守护线程有什么区别 程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线

C#中的各种集合(未完待续)

泛型集合类&非泛型集合类 泛型集合类:属于强类型,主要是指在代码编译期间就进行检查.如List<string>,不是string类型的object是不可以添加到泛型的.Dictionary也是强类型. 非泛型集合类:属于弱类型,这类集合不会在编译期间就进行检查.在遍历期间可能会涉及到类型转换时,容易引起类型转换的异常.ArrayList,HashTable,等等. 泛型集合类:List<T>,Dictionary<T>,Queue<T>,Stack&

Linux系统下Java开发环境的配置(未完...)

1.查看jdk版本   java -version 2.将下载好的jdk放在/usr/lib/jvm里(其中jvm是自己起的名)   sudo mv jdk1.8.0_111 /usr/lib/jvm 3.配置环境变量(注意配置的是哪个用户,这个是配置的用户变量,而不是系统变量,系统变量用vim /etc/profile)   (1)打开bashrc   gedit ~/.bashrc (2)将下面配置信息加入bashrc   export JAVA_HOME=/usr/lib/jvm/jdk1

Java面试:用set集合的时候,重写过hashcode()和equal()方法吗?有什么作用?

首先Set接口的特点: 1.它不允许出现重复元素-----------无重复 2.不保证集合中元素的顺序---------无序 3.允许包含值为null的元素,但最多只能有一个null元素. HashSet集合,采用哈希表结构存储数据,保证元素唯一性的方式依赖于:hashCode()与equals()方法. 1)HashSet集合排重时,需要判断两个对象是否相同,对象相同的判断可以通过hashCode值判断,所以需要重写hashCode()方法 2)hashset不能为一样的,放入一个值首先判断

java开发设计数据库细节(未完待续)

1.create_time(创建时间)不能勾选"根据当前时间戳更新",否则每更新一次信息,创建时间就会改变一次.2.update_time(最后一次更新时间)一定要勾选"根据当前时间戳更新",否则每次更新信息,update_time都是不会改变的 原文地址:https://blog.51cto.com/13678728/2456635

【JAVA秒会技术之秒杀面试官】秒杀Java面试官——集合篇(一)

[JAVA秒会技术之秒杀面试官]秒杀Java面试官--集合篇(一) [JAVA秒会技术之秒杀面试官]JavaEE常见面试题(三) http://blog.csdn.net/qq296398300/article/category/6876287

我的Java问题集(1)(重点、难点、疑点)未完待续...

学习Java这么久了,总算也了点笔记,一个一个字码的哟! 1.Java中的int等类型变量既然已经有默认值为0,为什么还会出现没有初始化,编译出错呢? 一个变量作为类成员使用的时候,如果没有被初始化,java会为其分配默认值: Boolean false Char '\u0000'(null) byte (byte)0 short (short)0 int 0 long 0L float 0.0f double 0.0d 如果在一个方法中定义一个变量,java不会给其分配默认值,就必须我们来给他

java 面试 -- 4

Java面试知识点总结 本篇文章会对面试中常遇到的Java技术点进行全面深入的总结,帮助我们在面试中更加得心应手,不参加面试的同学也能够借此机会梳理一下自己的知识体系,进行查漏补缺(阅读本文需要有一定的Java基础:若您初涉Java,可以通过这些问题建立起对Java初步的印象,待有了一定基础后再后过头来看收获会更大).本文的问题列表来自于http://www.nowcoder.com/discuss/3043,在此感谢原作者的无私分享:) 1. Java中的原始数据类型都有哪些,它们的大小及对应

Java 面试-- 1

JAVA面试精选[Java基础第一部分] 这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对于那些正打算找工作JAVA软件开发工作的童鞋们来说,当你看到这份题目的时候,你应该感动很幸运,因为,只要你把题目中的内容都搞懂了,在笔试的时候就可以游刃有余,通过面试只有半步之遥了,笔试只能反映你的JAVA技能.不管你是面试各个级别的JAVA工程师.架构师.还是项目经理,这个系列文章都是你最宝贵的资