ArrayList与linkedList面试事项

ArrayList 和linkedList 也是面试中经常遇到的问题,也是平时开发中最常用的list,了解这两个的特性并恰当区分使用,可以一定程度上提高代码的执行效率。今个记一下这两个的使用方法,并总结区别。

ArrayList

arraylist就是一个数组,一个动态数组,可以动态改变元素,自定义数组大小。实现了RandomAcess,Cloneable、Serializable接口。

一、先简单看一下构造方法:

1.public ArrayList();

2.public ArrayList(ICollection);

3.public ArrayList(int);

第一种:默认的构造器,初始化一个容量为10的数组。

第二种:传入一个实现ICollection的对象,并将传入对象中的元素赋值给新建的list;

第三种:自定义数组的容量。

二、Arraylist中的方法

其中有get、set、add、indexof,contains,clone、size、toarray、isEmpty、sort等方法的使用,我觉得不是什么难点,api介绍很清楚,在后续比较与linkedlist区别时会介绍一些,现在稍微总结一下:

1.当add方法一直添加,知道超过现有容量大小时,arraylist重新设置的容量为(原始容量*3)/2+1,也就是原本容量增加1/2

2.arraylist实现了Cloneable接口,中心就是其中的clone方法。可以将此对象的全部元素复制到另一个数组中。

3.arraylist实现Serializable接口,是可序列化的,当读写的时候,会容量先行,即:写入的时候,先写入容量,在放置元素;读取时,先读取容量,在去除元素。需要注意的是,可序列化只是一个标志,意味着它可以被序列化,并没有实际性的方法、字段,但是这不代表没有意义,因为在上述读写过程中,就会依次进行。我还没有了解深入,但事实就是这样。后续在补充吧。

三、ArrayList的遍历

谈到数组,集合。免不了使用遍历,

有三种方法可以遍历ArrayList;

1.迭代器

部分代码:

Iterator iter = list.iterator();
while (iter.hasNext()) {
    value = (Integer)iter.next();

}

2.随机访问

for (int i=0; i<list.size; i++) {
    value = (Integer)list.get(i);        

}

3.foreach

for (Integer integ:list) {
    value = integ;

}


测试一下,就能知道他们之间的效率 随机访问>foreach>迭代器。这算是ArrayList的最大优点。

LinkedList

LinkedList是一个双向链表,可以被当做堆栈、队列或者双向队列。实现了list、Deque、Cloneable、Serializable接口。

一、为数不多的三个属性:

1、size,记录当前list有多少节点。

2、first,代表当前第一个节点。

3、last,代表当前最后一个节点。

后面的方法都与这仨属性息息相关。

方法

 
 

、构造方法:

1.public LinkedList() {

}

2.

public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);

}

只有两个,与arraylist中对应的两个用法相同。

三、类中方法

1.add

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

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}
此方法就是将新的元素放到链表最后,长度加1,修改次数加1;

2.带参add

public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}
很清晰,不赘述。

3.get

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
首先判断有没有超出索引长度。然后和长度的1/2对比,小于就从头遍历,大于就从尾部遍历。

linkedlist不能快速访问,只能挨个遍历,为了尽可能少的遍历,判断从头部开始还是尾部开始是个很好的方法

4.remove

public E remove() {
    return removeFirst();
}
public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}

private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}
即:remove就是removefirst,将第一个节点设为null,并将全局变量first设置为当前节点的下一个节点。长度减一,修改次数加一

5.removelast

private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}
将最后一个节点设为null,将原本最后一个节点的上一个节点设为全局变量last,长度减一,修改次数加一。

6.remove(object o)

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

从first到last循环遍历,如果参数和当前元素相等,调用unlink(下面解释此方法)

7.remove(int index)

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}
 
 
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
先调用node方法,找到索引对应的值,之后和remove(object o)相同
E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}
这个方法的思路是获取要移除节点的上一节点和下一节点,
如果上一节点是空,则list的first节点为当前节点的下一节点,
如果下一节点为空,list的last节点为当前节点的上一节点。
如果都不是,把当前节点设为null,这样当前节点的上一节点指向当前节点的下一节点。

8.toarray

public Object[] toArray() {
    Object[] result = new Object[size];
    int i = 0;
    for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;
    return result;
}

遍历当前list,并挨个赋值到新的数组中。


区别

从上面的分析已经可以大致看出两者的区别了,总结一下:

linkedlist是基于链表的list,从大量的成员方法也能看出,添加,删除占了大比重,相比Arraylist的增删,linkedlist更高效,因为没有list容量的问题,不需要再添加时判断容量是否超出;删除时直接删除元素,上一节点会指向下一节点,而arraylist删除指定元素,后面的所有元素都必须向前移动相应位置。

ArrayList的快速访问是一个优势,从上面linkedlist的方法中可以看到,遍历占据了大量的份额,任何的查询,大部分的删除都得在真正的行为开始前进行所有元素的遍历。而作为数组的arrayList,可以通过索引,直接访问到对应的元素。

总而言之,arraylist更适合读取,linkedlist更适合增删。

猜你喜欢

转载自blog.csdn.net/wy9717/article/details/80061266
今日推荐