参考:
https://blog.csdn.net/qq_38304320/article/details/103737343
https://blog.csdn.net/kai3123919064/article/details/90343320
https://blog.csdn.net/nisemono_ct/article/details/95620007
LinkedListed的add(index,element)源码解析
LinkedList<Integer> list = new LinkedList<>();
list.add(0,1);
System.out.println(list);
list.add(1,2);
System.out.println(list);
list.add(1,3);
System.out.println(list);
list.add(0,4);
System.out.println(list);
list.add(2,5);
System.out.println(list);
执行结果:
[1]
[1, 2]
[1, 3, 2]
[4, 1, 3, 2]
[4, 1, 5, 3, 2]
public void add(int index, E element) { //检查index是否合法,即 index>=0 && index <=size checkPositionIndex(index); //如果index为链表大小,说明只需要尾节点直接插入即可 if (index == size) linkLast(element); else //在index之前插入元素 linkBefore(element, node(index)); }
linkLast方法,正常插入方法
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++; }
//节点的数据结构
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; } }
linkBefore方法
/** * Inserts element e before non-null Node succ. */ void linkBefore(E e, Node<E> succ) { // assert succ != null; //succ的前驱节点为pred eg:1->2 2的前驱节点为1 final Node<E> pred = succ.prev; //生成一个新的节点newNode 通过 succ,succ的前驱节点,以及元素e final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; // 如果前驱节点为null,那么将新节点直接弄在第一个节点上 /* eg: 1->2->3 list.add(0,4) succ=1 pred = null 将4直接插在第一个节点上 新链表为 4->1->2->3 */ if (pred == null) first = newNode; else //如果前驱节点不null /* eg: 4->1->2->3 list.add(2,5) succ=2 pred = 1 将5pred即节点值为1的后面 新链表为 4->1->5->2->3 */ pred.next = newNode; size++; modCount++; }
node(index)方法
返回指定索引的节点
首先后判断索引是否小于总数的一半,如果小于则会正向遍历,否则会反向遍历
比如现在链表为 1 -> 2 -> 3 -> 4
node(1):2
node(2):3
/** * Returns the (non-null) Node at the specified element index. */ Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++)//每次迭代前都会测试布尔表达式:i<index,所以如果index=0,x=x.next不执行,返回first节点 x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
总结
LinkedList list = new LinkedList<>();
list.add(0,1); list: 1
list.add(1,2); list: 1->2
list.add(1,3); list: 1-3->2
list.add(0,4); list: 4->1->3->2
list.add(2,5); list: 4->1->5->3->2
Linkedlist的add()和get()方法源码解析
add()方法
- 调用add()方法添加元素,会调用linkedlast(),把新添加的元素放在原链表最后一位
- 首先获取原链表最后一个元素,根据当前输入的元素构建一个node,把原最后一位元素最为新构建的元素中pre对应的变量
- 新构建的node覆盖之前最后一个node变量
- 判断为添加前的最后一位是否为空,如果为空,新添加的一个node也是first变量对应的元素
- 如果添加前的最后一位不为空,则未添加前的最后一位元素的next变量被新添加的元素覆盖
- 链表长度加1,链表修改统计加1
public boolean add(E e) { // 调用 linkLast方法,把新添加的元素添加到链表的最后一个位置 linkLast(e); return true; }
// 把元素添加在链表的最后一个位置的方法 void linkLast(E e) { // 获取链表中最后一个元素 final Node<E> l = last; // 该方法是把元素放在最后一个位置,所以创建一个新的node,变量前一个元素是没添加前最后一个 final Node<E> newNode = new Node<>(l, e, null); // 把新添加的元素作为最后一个 last = newNode; // 判断新元素未添加前的最后一个是否是空,如果是,则新添加的元素即是最后一个,也是第一个 if (l == null) // 新添加的元素赋给第一个 first = newNode; else // 如果不为空,则原来最有一个元素的next遍历绑定新添加的元素 l.next = newNode; // 链表长度加1,修改次数加1 size++; modCount++; }
get()方法
- 首先,校验输入的下标是否满足linkedlist中的长度范围,如果不满足,抛出异常
- 如果满足,调用node()方法,首先把原长度除以2,判断当前输入的下标是在上半部分还是下半部分
- 如果在上半部分,从上往下遍历,如果在下半部分,从后往前遍历.
- 最后一个元素就是要找的元素
public E get(int index) { // 检查索引是否规范 checkElementIndex(index); // 获取指定下标的元素 return node(index).item; } // 判断索引是否规范 private void checkElementIndex(int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } // 判断索引是否在链表长度的范围内 private boolean isElementIndex(int index) { return index >= 0 && index < size; } Node<E> node(int index) { // 把链表的长度向右移一个单位,也就是除以2, 判断录入的索引是在前半段还是后半段 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; } }
链表转数组:toArray()、toArray(T[] a)
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; }
以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
public <T> T[] toArray(T[] a) { if (a.length < size)//数组a长度过短,重新生成size长度的数组 a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; if (a.length > size)//数组a长度过长,让超过size长度的数据被GC a[size] = null; return a; }
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
参考例子和执行结果:
String[] planets=new String[]{"Mecury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"}; LinkedList linklisted=new LinkedList(Arrays.asList(planets)); linklisted.add(3,"Position"); System.out.println("linkdelist"+linklisted.toString()); String[] newPlanets0= (String[]) linklisted.toArray(new String [0]);//少于size长度会重新实例化size长度的数组 System.out.println("ArrayList0"+Arrays.toString(newPlanets0)); String[] newPlanets1= (String[]) linklisted.toArray(new String [20]);//高于size长度直接使用此数组,超出链表长度的值为null System.out.println("ArrayList1"+Arrays.toString(newPlanets1)); String[] newPlanets2 = (String[]) linklisted.toArray(new String[linklisted.size()]);//相同长度对性能和内存最友好 System.out.println("ArrayList2"+Arrays.toString(newPlanets2)); Object[] newPlanets3 = linklisted.toArray();//toArray返回的是Object数组,不能转到String类型 System.out.println("ArrayList3"+Arrays.toString(newPlanets3));
执行结果:
linkdelist[Mecury, Venus, Earth, Position, Mars, Jupiter, Saturn, Uranus, Neptune] ArrayList0[Mecury, Venus, Earth, Position, Mars, Jupiter, Saturn, Uranus, Neptune] ArrayList1[Mecury, Venus, Earth, Position, Mars, Jupiter, Saturn, Uranus, Neptune, null, null, null, null, null, null, null, null, null, null, null] ArrayList2[Mecury, Venus, Earth, Position, Mars, Jupiter, Saturn, Uranus, Neptune] ArrayList3[Mecury, Venus, Earth, Position, Mars, Jupiter, Saturn, Uranus, Neptune]
数组转链表:
LinkedList linklist=new LinkedList(Arrays.asList(array));
/** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
数组转换链表的是调用了Arrays.asList方法
static List asList(T… a)
返回由指定数组支持的固定大小的列表。