提示:以下是本篇文章正文内容,下面案例可供参考
在集合类中,List是最基础的一种集合:它是一种有序列表。List的行为和数组几乎完全相同;List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,ListList的索引和数组一样,从0开始,对于List接口,我们日常使用比较多的实现类便是ArrayList和LinkedList。
实现List接口并非只能通过数组(即ArrayList的实现方式)来实现,另一种LinkedList通过“链表”也实现了List接口。在LinkedList中,它的内部每个元素都指向下一个元素:
相较于ArrayList而言,LinkedList 的底层数据结构为双向链表,而并非数组的形式,所以在一些特定情况下会存在一些差异:
代码如下(示例):
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
public abstract class AbstractSequentialList<E> extends AbstractList<E>
如图所示,LinkedList继承了AbstractSequentialList类,AbstractSequentialList类又继承自 AbstractList类,相比于ArrayList 其上面多了一层 AbstractSequentialList,而这个抽象类继承自 AbstractList。相比而言,AbstractSequentialList为顺序存取结构AbstractList 则是随机存取,如果想要实现顺序存取应该继承此抽象类,而随机存取应该继承 AbstractList。
代码如下(示例):
// LinkedList集合元素个数(逻辑大小)
transient int size = 0;
// 双向链表的头节点
transient Node<E> first;
// 双向链表的尾节点
transient Node<E> last;
//版本号
private static final long serialVersionUID = 876323262645176354L;
代码如下(示例):
//无参构造
public LinkedList() {
}
//有参构造,将传入的集合 c中的元素初始化为 LinkedList 的元素
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
2.3.1 add()方法
public boolean add(E e) {
linkLast(e);// 调用 linkLast 方法将元素连接在链表尾部
return true;
}
public void addLast(E e) {
linkLast(e);// 调用 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++;
}
从 linkLast 可以看到在调用 add() 方法后会创建一个新节点并连接到原来链表的最尾部,此过程只需要创建一个新的 node 对象并修改若干指针,相比于 ArrayList 的数组复制 LinkedList 在添加元素方面更加高效。
public void add(int index, E element) {
checkPositionIndex(index);// 判断 index >= 0 && index <= size (下标是否合法)
if (index == size)// 如果 index == size 直接在链表尾部添加元素
linkLast(element);
else //否则,调用 linkBefore() 方法在指定 index 处插入元素
linkBefore(element, node(index));
}
//在指定位置插入元素节点
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
public void addFirst(E e) {
linkFirst(e);
}
//以链表的结构通过头插法添加数据
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//将集合c依次添加至LinkedList集合中,从LinkedList集合的**尾部**添加
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);// 确保索引不越界
Object[] a = c.toArray();//将集合c转为Object[]数组临时保存
int numNew = a.length;
if (numNew == 0) //如果新数组长度为0,则结束添加操作
return false;
Node<E> pred, succ;
if (index == size) {// 如果 index == size 直接在末尾添加
succ = null;
pred = last;
} else { // 在中间插入,先取得 index 对应的 node 节点,即其前一个节点
succ = node(index);
pred = succ.prev;
}
// 在中间插入,先取得 index 对应的 node 节点,即其前一个节点
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
2.3.2 get()方法
public E get(int index) {
checkElementIndex(index);//检查索引合法
return node(index).item;//返回当前索引节点的值
}
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();//如果头元素为空,引发迭代异常
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();//如果尾元素为空,引发迭代异常
return l.item;
}
2.3.3 remove()方法
public E remove() {
return removeFirst();//调用removeFirst()方法,删除头元素
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();//如果头元素为空,引发迭代异常
return unlinkFirst(f);//调用unlinkFirst()方法,返回指定元素
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();//如果尾元素为空,引发迭代异常
return unlinkLast(l);//调用unlinkFirst()方法,返回指定元素
}
public boolean remove(Object o) {
if (o == null) { // 判断传入元素是否为 null
for (Node<E> x = first; x != null; x = x.next) {// 遍历链表寻找相等的元素
if (x.item == null) {
// remove() 方法的核心 unlink() 其作用为将一个指定的节点从链表中删除
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;
}
//将一个指定的节点从链表中删除
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 {
// 将当前节点的前一个节点的 next 指向当前节点的后一个节点
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
// 将当前节点的后一个节点的 prev 指向当前节点的前一个节点
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
public E remove(int index) {
checkElementIndex(index);//检查索引合法
return unlink(node(index));//再调用node()方法和unlink()方法进行指定位置元素的删除
}
2.3.4set()方法
public E set(int index, E element) {
checkElementIndex(index);//检查索引合法
Node<E> x = node(index);// 通过 node() 方法来的到 index 所对应的那个节点
E oldVal = x.item;
x.item = element; // 设置节点中新的元素值
return oldVal;
}
2.3.5clear()方法
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null; ) { //遍历链表,将每一个节点中的元素转换为null
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
2.3.6contains方法
public boolean contains(Object o) {
return indexOf(o) != -1;//调用indexO方法,如果返回值不等于-1,则存在
}
2.3.7indexOf()方法
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) { //遍历链表,逐个查找
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
2.3.8sort()方法
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray(); //将链表转换为Object类型的数组
Arrays.sort(a, (Comparator) c);//通过比较器定义的比较方式进行数组排序
ListIterator<E> i = this.listIterator();//定义迭代器
for (Object e : a) { //遍历
i.next();
i.set((E) e);
}
}
2.3.9toArray()方法
public <T> T[] toArray(T[] a) {
if (a.length < 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] = null;
return a;
}
private class ListItr implements ListIterator<E>
public interface ListIterator<E> extends Iterator<E>
ListItr 内部类实现了 ListIterator 接口,ListIterator 接口继承自 Iterator 接口,,它为LinkedList实现了迭代器的功能。
public class LinkedList<E>implements Deque<E>
LinkedList 不仅实现了 List 接口还实现了 Deque 接口,因此具有双端队列的所有特征,是双端队列的一种实现,可以用作栈,队列,双端队列来使用,因此,LinkedList也具有很多队列相关的操作。
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public boolean offer(E e) {
return add(e);
}
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
1.LinkedList 的本质是一个双向链表,通过内部类 Node 来实现这种结构。
2.LinkedList 相比ArrayList,其理论上可以无限扩展,与之相对的是在查询上性能较差。
3.LinkedList更适用于写多读少的场景,适合数据的频繁添加删除等操作。
4.LinkedList也实现了Dqueue接口,可以用来定义无界队列。
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。