LinkedList也和ArrayList一样实现了List接口,但是它执行插入和删除操作时比ArrayList更加高效,因为它是基于链表的。基于链表也决定了它在随机访问方面要比ArrayList逊色一点。

除此之外,LinkedList还提供了一些可以使其作为栈、队列、双端队列的方法。这些方法中有些彼此之间只是名称的区别,以使得这些名字在特定的上下文中显得更加的合适。

先看LinkedList类的定义。

1 public class LinkedList<E>
2 extends AbstractSequentialList<E>
3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList继承自AbstractSequenceList、实现了List及Deque接口。其实AbstractSequenceList已经实现了List接口,这里标注出List只是更加清晰而已。AbstractSequenceList提供了List接口骨干性的实现以减少实现List接口的复杂度。Deque接口定义了双端队列的操作。

LinkedList中之定义了两个属性:

1 private transient Entry<E> header = new Entry<E>(null, null, null);
2 private transient int size = 0;

size肯定就是LinkedList对象里面存储的元素个数了。LinkedList既然是基于链表实现的,那么这个header肯定就是链表的头结点了,Entry就是节点对象了。一下是Entry类的代码。

 1 private static class Entry<E> {
2 E element;
3 Entry<E> next;
4 Entry<E> previous;
5
6 Entry(E element, Entry<E> next, Entry<E> previous) {
7 this.element = element;
8 this.next = next;
9 this.previous = previous;
10 }
11 }

只定义了存储的元素、前一个元素、后一个元素,这就是双向链表的节点的定义,每个节点只知道自己的前一个节点和后一个节点。

来看LinkedList的构造方法。

1 public LinkedList() {
2 header.next = header.previous = header;
3 }
4 public LinkedList(Collection<? extends E> c) {
5 this();
6 addAll(c);
7 }

LinkedList提供了两个构造方法。第一个构造方法不接受参数,只是将header节点的前一节点和后一节点都设置为自身(注意,这个是一个双向循环链表,如果不是循环链表,空链表的情况应该是header节点的前一节点和后一节点均为null),这样整个链表其实就只有header一个节点,用于表示一个空的链表。第二个构造方法接收一个Collection参数c,调用第一个构造方法构造一个空的链表,之后通过addAll将c中的元素全部添加到链表中。来看addAll的内容。

 1 public boolean addAll(Collection<? extends E> c) {
2 return addAll(size, c);
3 }
4 // index参数指定collection中插入的第一个元素的位置
5 public boolean addAll(int index, Collection<? extends E> c) {
6 // 插入位置超过了链表的长度或小于0,报IndexOutOfBoundsException异常
7 if (index < 0 || index > size)
8 throw new IndexOutOfBoundsException("Index: "+index+
9 ", Size: "+size);
10 Object[] a = c.toArray();
11 int numNew = a.length;
12 // 若需要插入的节点个数为0则返回false,表示没有插入元素
13 if (numNew==0)
14 return false;
15 modCount++;
16 // 保存index处的节点。插入位置如果是size,则在头结点前面插入,否则获取index处的节点
17 Entry<E> successor = (index==size ? header : entry(index));
18 // 获取前一个节点,插入时需要修改这个节点的next引用
19 Entry<E> predecessor = successor.previous;
20 // 按顺序将a数组中的第一个元素插入到index处,将之后的元素插在这个元素后面
21 for (int i=0; i<numNew; i++) {
22 // 结合Entry的构造方法,这条语句是插入操作,相当于C语言中链表中插入节点并修改指针
23 Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
24 // 插入节点后将前一节点的next指向当前节点,相当于修改前一节点的next指针
25 predecessor.next = e;
26 // 相当于C语言中成功插入元素后将指针向后移动一个位置以实现循环的功能
27 predecessor = e;
28 }
29 // 插入元素前index处的元素链接到插入的Collection的最后一个节点
30 successor.previous = predecessor;
31 // 修改size
32 size += numNew;
33 return true;
34 }

构造方法中的调用了addAll(Collection<? extends E> c)方法,而在addAll(Collection<? extends E> c)方法中仅仅是将size当做index参数调用了addAll(int index,Collection<? extends E> c)方法。

 1 private Entry<E> entry(int index) {
2 if (index < 0 || index >= size)
3 throw new IndexOutOfBoundsException("Index: "+index+
4 ", Size: "+size);
5 Entry<E> e = header;
6 // 根据这个判断决定从哪个方向遍历这个链表
7 if (index < (size >> 1)) {
8 for (int i = 0; i <= index; i++)
9 e = e.next;
10 } else {
11 // 可以通过header节点向前遍历,说明这个一个循环双向链表,header的previous指向链表的最后一个节点,这也验证了构造方法中对于header节点的前后节点均指向自己的解释
12 for (int i = size; i > index; i--)
13 e = e.previous;
14 }
15 return e;
16 }

结合上面代码中的注释及双向循环链表的知识,应该很容易理解LinkedList构造方法所涉及的内容。下面开始分析LinkedList的其他方法。

add(E e)

1 public boolean add(E e) {
2 addBefore(e, header);
3 return true;
4 }

从上面的代码可以看出,add(E e)方法只是调用了addBefore(E e,Entry<E> entry)方法,并且返回true。

addBefore(E e,Entry<E> entry)

1 private Entry<E> addBefore(E e, Entry<E> entry) {
2 Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
3 newEntry.previous.next = newEntry;
4 newEntry.next.previous = newEntry;
5 size++;
6 modCount++;
7 return newEntry;
8 }

addBefore(E e,Entry<E> entry)方法是个私有方法,所以无法在外部程序中调用(当然,这是一般情况,你可以通过反射上面的还是能调用到的)。

addBefore(E e,Entry<E> entry)先通过Entry的构造方法创建e的节点newEntry(包含了将其下一个节点设置为entry,上一个节点设置为entry.previous的操作,相当于修改newEntry的“指针”),之后修改插入位置后newEntry的前一节点的next引用和后一节点的previous引用,使链表节点间的引用关系保持正确。之后修改和size大小和记录modCount,然后返回新插入的节点。

总结,addBefore(E e,Entry<E> entry)实现在entry之前插入由e构造的新节点。而add(E e)实现在header节点之前插入由e构造的新节点。

add(int index,E e)

1 public void add(int index, E element) {
2 addBefore(element, (index==size ? header : entry(index)));
3 }

也是调用了addBefore(E e,Entry<E> entry)方法,只是entry节点由index的值决定。

构造方法,addAll(Collection<? extends E> c),add(E e),addBefor(E e,Entry<E> entry)方法可以构造链表并在指定位置插入节点,为了便于理解,下面给出插入节点的示意图。

addFirst(E e)

1 public void addFirst(E e) {
2 addBefore(e, header.next);
3 }

addLast(E e)

1 public void addLast(E e) {
2 addBefore(e, header);
3 }

看上面的示意图,结合addBefore(E e,Entry<E> entry)方法,很容易理解addFrist(E e)只需实现在header元素的下一个元素之前插入,即示意图中的一号之前。addLast(E e)只需在实现在header节点前(因为是循环链表,所以header的前一个节点就是链表的最后一个节点)插入节点(插入后在2号节点之后)。

clear()

 1 public void clear() {
2 Entry<E> e = header.next;
3 // e可以理解为一个移动的“指针”,因为是循环链表,所以回到header的时候说明已经没有节点了
4 while (e != header) {
5 // 保留e的下一个节点的引用
6 Entry<E> next = e.next;
7 // 接触节点e对前后节点的引用
8 e.next = e.previous = null;
9 // 将节点e的内容置空
10 e.element = null;
11 // 将e移动到下一个节点
12 e = next;
13 }
14 // 将header构造成一个循环链表,同构造方法构造一个空的LinkedList
15 header.next = header.previous = header;
16 // 修改size
17 size = 0;
18 modCount++;
19 }

上面代码中的注释已经足以解释这段代码的逻辑,需要注意的是提到的“指针”仅仅是概念上的类比,Java并不存在“指针”的概念,而只有引用,为了便于理解所以部分说明使用了“指针”。

contains(Object o)

1 public boolean contains(Object o) {
2 return indexOf(o) != -1;
3 }

仅仅只是判断o在链表中的索引。先看indexOf(Object o)方法。

 1 public int indexOf(Object o) {
2 int index = 0;
3 if (o==null) {
4 for (Entry e = header.next; e != header; e = e.next) {
5 if (e.element==null)
6 return index;
7 index++;
8 }
9 } else {
10 for (Entry e = header.next; e != header; e = e.next) {
11 if (o.equals(e.element))
12 return index;
13 index++;
14 }
15 }
16 return -1;
17 }

indexOf(Object o)判断o链表中是否存在节点的element和o相等,若相等则返回该节点在链表中的索引位置,若不存在则放回-1。

contains(Object o)方法通过判断indexOf(Object o)方法返回的值是否是-1来判断链表中是否包含对象o。

element()

1 public E element() {
2 return getFirst();
3 }

getFirst()

1 public E getFirst() {
2 if (size==0)
3 throw new NoSuchElementException();
4 return header.next.element;
5 }

element()方法调用了getFirst()返回链表的第一个节点的元素。为什么要提供功能一样的两个方法,像是包装了一下名字?其实这只是为了在不同的上下文“语境”中能通过更贴切的方法名调用罢了。

get(int index)

1 public E get(int index) {
2 return entry(index).element;
3 }

get(int index)方法用于获得指定索引位置的节点的元素。它通过entry(int index)方法获取节点。entry(int index)方法遍历链表并获取节点,在上面有说明过,不再陈述。

set(int index,E element)

1 public E set(int index, E element) {
2 Entry<E> e = entry(index);
3 E oldVal = e.element;
4 e.element = element;
5 return oldVal;
6 }

先获取指定索引的节点,之后保留原来的元素,然后用element进行替换,之后返回原来的元素。

getLast()

1 public E getLast()  {
2 if (size==0)
3 throw new NoSuchElementException();
4 return header.previous.element;
5 }

getLast()方法和getFirst()方法类似,只是获取的是header节点的前一个节点的元素。因为是循环链表,所以header节点的前一节点就是链表的最后一个节点。

lastIndexOf(Object o)

 1 public int lastIndexOf(Object o) {
2 int index = size;
3 if (o==null) {
4 for (Entry e = header.previous; e != header; e = e.previous) {
5 index--;
6 if (e.element==null)
7 return index;
8 }
9 } else {
10 for (Entry e = header.previous; e != header; e = e.previous) {
11 index--;
12 if (o.equals(e.element))
13 return index;
14 }
15 }
16 return -1;
17 }

因为查找的是last index,即最后一次出现的位置,所以采用由后向前的遍历方式。因为采用了有后向前的遍历,所以index被赋值为size,并且循环体内执行时都进行减操作。分两种情况判断是否存在,分别是null和不为空。

offer(E e)

1 public boolean offer(E e) {
2 return add(e);
3 }

在链表尾部插入元素。

offerFirst(E e)

1 public boolean offerFirst(E e) {
2 addFirst(e);
3 return true;
4 }

在链表开头插入元素。

offerLast(E e)

1 public boolean offerLast(E e) {
2 addLast(e);
3 return true;
4 }

在链表末尾插入元素。

上面这三个方法都只是调用了相应的add方法,同样只是提供了不同的方法名在不同的语境下使用。

peek()

1 public E peek() {
2 if (size==0)
3 return null;
4 return getFirst();
5 }

peekFirst()

1 public E peekFirst() {
2 if (size==0)
3 return null;
4 return getFirst();
5 }

peekLast()

1 public E peekLast() {
2 if (size==0)
3 return null;
4 return getLast();
5 }

上面的三个方法也很简单,只是调用了对应的get方法。

poll()

1 public E poll() {
2 if (size==0)
3 return null;
4 return removeFirst();
5 }

pollFirst()

1 public E pollFirst() {
2 if (size==0)
3 return null;
4 return removeFirst();
5 }

pollLast()

1 public E pollLast() {
2 if (size==0)
3 return null;
4 return removeLast();
5 }

poll相关的方法都是获取并移除某个元素。都是和remove操作相关。

pop()

1 public E pop() {
2 return removeFirst();
3 }

push(E e)

1 public void push(E e) {
2 addFirst(e);
3 }

这两个方法对应栈的操作,即弹出一个元素和压入一个元素,仅仅是调用了removeFirst()和addFirst()方法。

下面集中看remove相关操作的方法。

remove()

1 public E remove() {
2 return removeFirst();
3 }

remove(int index)

1 public E remove(int index) {
2 return remove(entry(index));
3 }

remove(Object o)

 1 public boolean remove(Object o) {
2 if (o==null) {
3 for (Entry<E> e = header.next; e != header; e = e.next) {
4 if (e.element==null) {
5 remove(e);
6 return true;
7 }
8 }
9 } else {
10 for (Entry<E> e = header.next; e != header; e = e.next) {
11 if (o.equals(e.element)) {
12 remove(e);
13 return true;
14 }
15 }
16 }
17 return false;
18 }

removeFirst()

1 public E removeFirst() {
2 return remove(header.next);
3 }

removeLast()

1 public E removeLast() {
2 return remove(header.previous);
3 }

removeFirstOccurrence()

1 public boolean removeFirstOccurrence(Object o) {
2 return remove(o);
3 }

removeLastOccurence()

 1 public boolean removeLastOccurrence(Object o) {
2 if (o==null) {
3 for (Entry<E> e = header.previous; e != header; e = e.previous) {
4 if (e.element==null) {
5 remove(e);
6 return true;
7 }
8 }
9 } else {
10 for (Entry<E> e = header.previous; e != header; e = e.previous) {
11 if (o.equals(e.element)) {
12 remove(e);
13 return true;
14 }
15 }
16 }
17 return false;
18 }

几个remove方法最终都是调用了一个私有方法:remove(Entry<E> e),只是其他简单逻辑上的区别。下面分析remove(Entry<E> e)方法。

 1 private E remove(Entry<E> e) {
2 if (e == header)
3 throw new NoSuchElementException();
4 // 保留将被移除的节点e的内容
5 E result = e.element;
6 // 将前一节点的next引用赋值为e的下一节点
7 e.previous.next = e.next;
8 // 将e的下一节点的previous赋值为e的上一节点
9 e.next.previous = e.previous;
10 // 上面两条语句的执行已经导致了无法在链表中访问到e节点,而下面解除了e节点对前后节点的引用
11 e.next = e.previous = null;
12 // 将被移除的节点的内容设为null
13 e.element = null;
14 // 修改size大小
15 size--;
16 modCount++;
17 // 返回移除节点e的内容
18 return result;
19 }

clone()

 1 public Object clone() {
2 LinkedList<E> clone = null;
3 try {
4 clone = (LinkedList<E>) super.clone();
5 } catch (CloneNotSupportedException e) {
6 throw new InternalError();
7 }
8 clone.header = new Entry<E>(null, null, null);
9 clone.header.next = clone.header.previous = clone.header;
10 clone.size = 0;
11 clone.modCount = 0;
12 for (Entry<E> e = header.next; e != header; e = e.next)
13 clone.add(e.element);
14 return clone;
15 }

调用父类的clone()方法初始化对象链表clone,将clone构造成一个空的双向循环链表,之后将header的下一个节点开始将逐个节点添加到clone中。最后返回克隆的clone对象。

toArray()

1 public Object[] toArray() {
2 Object[] result = new Object[size];
3 int i = 0;
4 for (Entry<E> e = header.next; e != header; e = e.next)
5 result[i++] = e.element;
6 return result;
7 }

创建大小和LinkedList相等的数组result,遍历链表,将每个节点的元素element复制到数组中,返回数组。

toArray(T[] a)

 1 public <T> T[] toArray(T[] a) {
2 if (a.length < size)
3 a = (T[])java.lang.reflect.Array.newInstance(
4 a.getClass().getComponentType(), size);
5 int i = 0;
6 Object[] result = a;
7 for (Entry<E> e = header.next; e != header; e = e.next)
8 result[i++] = e.element;
9 if (a.length > size)
10 a[size] = null;
11 return a;
12 }

先判断出入的数组a的大小是否足够,若大小不够则拓展。这里用到了发射的方法,重新实例化了一个大小为size的数组。之后将数组a赋值给数组result,遍历链表向result中添加的元素。最后判断数组a的长度是否大于size,若大于则将size位置的内容设置为null。返回a。

从代码中可以看出,数组a的length小于等于size时,a中所有元素被覆盖,被拓展来的空间存储的内容都是null;若数组a的length的length大于size,则0至size-1位置的内容被覆盖,size位置的元素被设置为null,size之后的元素不变。

为什么不直接对数组a进行操作,要将a赋值给result数组之后对result数组进行操作?

---------------------------------------------------------------------------------------------------------------------------------

LinkedList的Iterator

除了Entry,LinkedList还有一个内部类:ListItr。

ListItr实现了ListIterator接口,可知它是一个迭代器,通过它可以遍历修改LinkedList。

在LinkedList中提供了获取ListItr对象的方法:listIterator(int index)。

1 public ListIterator<E> listIterator(int index) {
2 return new ListItr(index);
3 }

该方法只是简单的返回了一个ListItr对象。

LinkedList中还有通过集成获得的listIterator()方法,该方法只是调用了listIterator(int index)并且传入0。

下面详细分析ListItr。

  1 private class ListItr implements ListIterator<E> {
2 // 最近一次返回的节点,也是当前持有的节点
3 private Entry<E> lastReturned = header;
4 // 对下一个元素的引用
5 private Entry<E> next;
6 // 下一个节点的index
7 private int nextIndex;
8 private int expectedModCount = modCount;
9 // 构造方法,接收一个index参数,返回一个ListItr对象
10 ListItr(int index) {
11 // 如果index小于0或大于size,抛出IndexOutOfBoundsException异常
12 if (index < 0 || index > size)
13 throw new IndexOutOfBoundsException("Index: "+index+
14 ", Size: "+size);
15 // 判断遍历方向
16 if (index < (size >> 1)) {
17 // next赋值为第一个节点
18 next = header.next;
19 // 获取指定位置的节点
20 for (nextIndex=0; nextIndex<index; nextIndex++)
21 next = next.next;
22 } else {
23 // else中的处理和if块中的处理一致,只是遍历方向不同
24 next = header;
25 for (nextIndex=size; nextIndex>index; nextIndex--)
26 next = next.previous;
27 }
28 }
29 // 根据nextIndex是否等于size判断时候还有下一个节点(也可以理解为是否遍历完了LinkedList)
30 public boolean hasNext() {
31 return nextIndex != size;
32 }
33 // 获取下一个元素
34 public E next() {
35 checkForComodification();
36 // 如果nextIndex==size,则已经遍历完链表,即没有下一个节点了(实际上是有的,因为是循环链表,任何一个节点都会有上一个和下一个节点,这里的没有下一个节点只是说所有节点都已经遍历完了)
37 if (nextIndex == size)
38 throw new NoSuchElementException();
39 // 设置最近一次返回的节点为next节点
40 lastReturned = next;
41 // 将next“向后移动一位”
42 next = next.next;
43 // index计数加1
44 nextIndex++;
45 // 返回lastReturned的元素
46 return lastReturned.element;
47 }
48
49 public boolean hasPrevious() {
50 return nextIndex != 0;
51 }
52 // 返回上一个节点,和next()方法相似
53 public E previous() {
54 if (nextIndex == 0)
55 throw new NoSuchElementException();
56
57 lastReturned = next = next.previous;
58 nextIndex--;
59 checkForComodification();
60 return lastReturned.element;
61 }
62
63 public int nextIndex() {
64 return nextIndex;
65 }
66
67 public int previousIndex() {
68 return nextIndex-1;
69 }
70 // 移除当前Iterator持有的节点
71 public void remove() {
72 checkForComodification();
73 Entry<E> lastNext = lastReturned.next;
74 try {
75 LinkedList.this.remove(lastReturned);
76 } catch (NoSuchElementException e) {
77 throw new IllegalStateException();
78 }
79 if (next==lastReturned)
80 next = lastNext;
81 else
82 nextIndex--;
83 lastReturned = header;
84 expectedModCount++;
85 }
86 // 修改当前节点的内容
87 public void set(E e) {
88 if (lastReturned == header)
89 throw new IllegalStateException();
90 checkForComodification();
91 lastReturned.element = e;
92 }
93 // 在当前持有节点后面插入新节点
94 public void add(E e) {
95 checkForComodification();
96 // 将最近一次返回节点修改为header
97 lastReturned = header;
98 addBefore(e, next);
99 nextIndex++;
100 expectedModCount++;
101 }
102 // 判断expectedModCount和modCount是否一致,以确保通过ListItr的修改操作正确的反映在LinkedList中
103 final void checkForComodification() {
104 if (modCount != expectedModCount)
105 throw new ConcurrentModificationException();
106 }
107 }

下面是一个ListItr的使用实例。

 1 LinkedList<String> list = new LinkedList<String>();
2 list.add("First");
3 list.add("Second");
4 list.add("Thrid");
5 System.out.println(list);
6 ListIterator<String> itr = list.listIterator();
7 while (itr.hasNext()) {
8 System.out.println(itr.next());
9 }
10 try {
11 System.out.println(itr.next());// throw Exception
12 } catch (Exception e) {
13 // TODO: handle exception
14 }
15 itr = list.listIterator();
16 System.out.println(list);
17 System.out.println(itr.next());
18 itr.add("new node1");
19 System.out.println(list);
20 itr.add("new node2");
21 System.out.println(list);
22 System.out.println(itr.next());
23 itr.set("modify node");
24 System.out.println(list);
25 itr.remove();
26 System.out.println(list);
 1 结果:
2 [First, Second, Thrid]
3 First
4 Second
5 Thrid
6 [First, Second, Thrid]
7 First
8 [First, new node1, Second, Thrid]
9 [First, new node1, new node2, Second, Thrid]
10 Second
11 [First, new node1, new node2, modify node, Thrid]
12 [First, new node1, new node2, Thrid]

LinkedList还有一个提供Iterator的方法:descendingIterator()。该方法返回一个DescendingIterator对象。DescendingIterator是LinkedList的一个内部类。

1 public Iterator<E> descendingIterator() {
2 return new DescendingIterator();
3 }

下面分析详细分析DescendingIterator类。

 1 private class DescendingIterator implements Iterator {
2 // 获取ListItr对象
3 final ListItr itr = new ListItr(size());
4 // hasNext其实是调用了itr的hasPrevious方法
5 public boolean hasNext() {
6 return itr.hasPrevious();
7 }
8 // next()其实是调用了itr的previous方法
9 public E next() {
10 return itr.previous();
11 }
12 public void remove() {
13 itr.remove();
14 }
15 }

LinkedList源码分析的更多相关文章

  1. LinkedList 的源码分析

    LinkedList是基于双向链表数据结构来存储数据的,以下是对LinkedList  的 属性,构造器 ,add(E e),remove(index),get(Index),set(inde,e)进 ...

  2. LinkedList的源码分析

    1. LinkedList的定义  1.1  继承于AbstractSequentialList的双向链表,可以被当作堆栈.队列或双端队列进行操作 1.2  有序,非线程安全的双向链表,默认使用尾部插 ...

  3. Java的LinkedList底层源码分析

    首先我们先说一下,源码里可以看出此类不仅仅用双向链表实现了队列数据结构的功能,还提供了链表数据结构的功能.

  4. LinkedList<E>源码分析

    LinkedList的数据结构就是双向链表,如下所示: private static class Node<E> { E item;//数据元素 Node<E> next;// ...

  5. LinkedList的源码分析(基于jdk1.8)

    1.初始化 public LinkedList() { } 并未开辟任何类似于数组一样的存储空间,那么链表是如何存储元素的呢? 2.Node类型 存储到链表中的元素会被封装为一个Node类型的结点.并 ...

  6. Java——LinkedList底层源码分析

    1.简介 LinkedList 是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢.另外,他还提供了 List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈 ...

  7. 集合框架——LinkedList集合源码分析

    目录 示例代码 底层代码 第1步(初始化集合) 第2步(往集合中添加一个元素) 第3步(往集合中添加第二个元素) 第4步(往集合中添加第三个元素) LinkedList添加元素流程示意图 第5步(删除 ...

  8. 集合之LinkedList(含JDK1.8源码分析)

    一.前言 LinkedList是基于链表实现的,所以先讲解一下什么是链表.链表原先是C/C++的概念,是一种线性的存储结构,意思是将要存储的数据存在一个存储单元里面,这个存储单元里面除了存放有待存储的 ...

  9. LinkedList源码分析和实例应用

    1. LinkedList介绍 LinkedList是继承于AbstractSequentialList抽象类,它也可以被当作堆栈.队列或者双端队列使用. LinkedList实现了Deque接口,即 ...

  10. [源码分析]ArrayList和LinkedList如何实现的?我看你还有机会!

    文章已经收录在 Github.com/niumoo/JavaNotes ,更有 Java 程序员所需要掌握的核心知识,欢迎Star和指教. 欢迎关注我的公众号,文章每周更新. 前言 说真的,在 Jav ...

随机推荐

  1. Opensuse enable sound and mic card

    Install application pavucontrol Run pavucontrol You will see the configuration about sound card and ...

  2. Java学习-042-获取目录文件列表(当前,级联)

    以下三个场景,在我们日常的测试开发中经常遇到: 软件自动化测试,在进行参数测试时,我们通常将所有相似功能的参数文件统一放在一个目录中,在自动化程序启动的时候,获取资源参数文件夹中所有参数文件,然后解析 ...

  3. C语言 ---- 数组 iOS学习-----细碎知识点总结

    #pragma mark - 数组:用来存放同一数据类型的数据 // 数组的定义:类型说明符 数组名[常量表达式] = {值1, 值2, 值3...};    // 定义一个float类型的数组,用来 ...

  4. ssm框架中的struts我的配置问题

    <?xml version="1.0" encoding="UTF-8" ?>  <!DOCTYPE struts PUBLIC    &qu ...

  5. angularJs自定义指令.directive==类似自定义标签

    创建自定义的指令 除了 AngularJS 内置的指令外,我们还可以创建自定义指令. 你可以使用 .directive 函数来添加自定义的指令. 要调用自定义指令,HTML 元素上需要添加自定义指令名 ...

  6. kafka 命令行操作

    1.创建主题(topic) bin/kafka-topics.sh --create --zookeeper m6:2181 --replication-factor 1 --partitions 1 ...

  7. cocos2dx 3.x(在Mac平台下利用Eclipse打包安卓apk安装包详细教程)

    最近在学习cocos2dx在MAC上如何打包apk,今天先把安装JDK和ANT的过程记来. 首先,打开终端,输入"java -version" 点击回车后,出现如下提示: 我们的M ...

  8. Android利用Java反射机制修改Android System Language

    private void updateLanguage(Locale locale) { try { Object objIActMag, objActMagNative; Class clzIAct ...

  9. 旋转toast 自定义toast方向,支持多个方向的显示,自定义View

    package com.example.canvasdemo; import java.security.InvalidAlgorithmParameterException; import andr ...

  10. CSS3 border-image详解、应用

    一.border-image的兼容性 border-image可以说是CSS3中的一员大将,将来一定会大放光彩,其应用潜力真的是非常的惊人.可惜目前支持的浏览器有限,仅Firefox3.5,chrom ...