前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Collection 集合源码剖析

Collection 集合源码剖析

作者头像
用户11097514
发布2024-05-30 21:24:22
600
发布2024-05-30 21:24:22
举报
文章被收录于专栏:技术分享技术分享

Collection

ArrayList源码剖析

添加元素分析

ArrayList数组源码剖析

三个构造器

代码语言:javascript
复制
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

/**
     * Constructs an empty list with an initial capacity of ten.
     */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
     * 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 ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

对于图中的拷贝如下图

add方法

他不仅可以实现添加,也可以实现插入操作

代码语言:javascript
复制
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++;
    }

以及对应的addAll方法,也可以是实现插入操作

代码语言:javascript
复制
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

Set方法

对于set方法,就像源码中提及的那样,如果判断索引位置数组下标没有越界,那么就直接赋值即可

代码语言:javascript
复制
public E set(int index, E element) {
    rangeCheck(index);//下标越界检查
    E oldValue = elementData(index);
    elementData[index] = element;//赋值到指定位置,复制的仅仅是引用
    return oldValue;
}

get方法

获取数据索引位置数据,检查是否越界。如果没有,然后返回即可

代码语言:javascript
复制
public E get(int index) {
    rangeCheck(index);
    return (E) elementData[index];//注意类型转换
}

remove方法

传入要删除位置的索引,然后将索引位置后面的元素全部向前挪一位即可(赋值拷贝)

代码语言:javascript
复制
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; //清除该位置的引用,让GC起作用
    return oldValue;
}

trimToSize()方法

作用 : 将底层数组的容量调整为当前列表保存的实际元素的大小的功能。 ,以减少内存的开销

代码语言:javascript
复制
/**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     */
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
            ? EMPTY_ELEMENTDATA
            : Arrays.copyOf(elementData, size);
    }
}

indexOf()—获取元素第一次出现的位置

代码语言:javascript
复制
/**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    public int indexOf(Object o) {
        //因为List可以存储null对象,所以源码地方在这里将其考虑进去,分开进行索引
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

lastIndexOf() — 获取元素最后一次出现的位置

从最后开始索引,然后出现的第一个就是最后一个。源码牛

代码语言:javascript
复制
/**
     * Returns the index of the last occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the highest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

LinkedList源码剖析

LinkedList<E> res = new LinkedList<>();

LinkedList同时实现了List接口和Deque接口,所以说我们既可以将其视为一个容器,又可以将其视为一个队列乃至栈

LinkedList底层通过双向链表实现

LinkedList通过firstlast引用分别指向链表的第一个和最后一个元素。注意这里没有所谓的哑元,当链表为空的时候firstlast都指向null

LinkedList它的内部定义了一个私有的Node链表

代码语言:javascript
复制
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;
    }
}

三个构造器

transient是Java语言的关键字,用来表示一个成员变量不是该对象序列化的一部分

代码语言:javascript
复制
public LinkedList() {
}

/**
 * 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);
}

/**
 * Links e as first element.
 */
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++;
}

/**
 * Links e as last element.
 */
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++;
}

getFirst() 获取第一个元素

只要对LinkedList一设置值,那么last 和first就会被赋值

代码语言:javascript
复制
/**
    * Returns the first element in this list.
    *
    * @return the first element in this list
    * @throws NoSuchElementException if this list is empty
    */
   public E getFirst() {
       final Node<E> f = first;
       if (f == null)
           throw new NoSuchElementException();
       return f.item;
   }

getLast() 获取最后一个元素:

代码语言:javascript
复制
/**
   * Returns the last element in this list.
   *
   * @return the last element in this list
   * @throws NoSuchElementException if this list is empty
   */
  public E getLast() {
      final Node<E> l = last;
      if (l == null)
          throw new NoSuchElementException();
      return l.item;
  }

删除元素(如下)

removeFirst(), removeLast(), remove(e), remove(index)

从源码中我们可以得出,如果需要删除某个节点,那么就将这个节点传入,或者传入该节点的索引

代码语言:javascript
复制
 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;
   }
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; // GC
       size--;
       modCount++;
       return element;
   }

对于removeFirst 和 removeLast

代码语言:javascript
复制
public E removeFirst() {
       final Node<E> f = first;
       if (f == null)
           throw new NoSuchElementException();
       return unlinkFirst(f);
   }


   /**
    * Unlinks non-null first node 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;
   }
代码语言:javascript
复制
public E removeLast() {
       final Node<E> l = last;
       if (l == null)
           throw new NoSuchElementException();
       return unlinkLast(l);
   }
   
   /**
    * Unlinks non-null last node l.
    */
   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;
   }

add方法

对于Add方法,官网给出了两类添加的方法,一种是直接添加到链表的末尾。还有一种是插入链表。

代码语言:javascript
复制
public boolean add(E e) {
       linkLast(e);
       return true;
   }
   
   /**
    * Links e as last element.
    */
   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++;
   }
代码语言:javascript
复制
public void add(int index, E element) {
    //先检查索引位置是否越界
        checkPositionIndex(index);
        if (index == size)
            linkLast(element);
        else
            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++;
    }

addAll()

同样,addAll也是有两种构造器,第一个是将另一个集合全部添加到链表的末尾,另一个是将另一个集合添加到指定的位置

代码语言:javascript
复制
public boolean addAll(Collection<? extends E> c) {
       return addAll(size, c);
   }

   /**
    * Inserts all of the elements in the specified collection into this
    * list, starting at the specified position.  Shifts the element
    * currently at that position (if any) and any subsequent elements to
    * the right (increases their indices).  The new elements will appear
    * in the list in the order that they are returned by the
    * specified collection's iterator.
    *
    * @param index index at which to insert the first element
    *              from the specified collection
    * @param c collection containing elements to be added to this list
    * @return {@code true} if this list changed as a result of the call
    * @throws IndexOutOfBoundsException {@inheritDoc}
    * @throws NullPointerException if the specified collection is null
    */
   public boolean addAll(int index, Collection<? extends E> c) {
       checkPositionIndex(index);

       Object[] a = c.toArray();
       int numNew = a.length;
       if (numNew == 0)
           return false;

       Node<E> pred, succ;
       if (index == size) {
           succ = null;
           pred = last;
       } else {
           succ = node(index);
           pred = succ.prev;
       }

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

Set方法

对于set方法,就像源码中提及的那样,如果判断索引位置数组下标没有越界,那么就直接赋值即可

代码语言:javascript
复制
/**
  * 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) {
     checkElementIndex(index);
     Node<E> x = node(index);
     E oldVal = x.item;
     x.item = element;
     return oldVal;
 }

get方法

获取数据索引位置数据,检查是否越界。如果没有,然后返回即可

代码语言:javascript
复制
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

 

Clear()

代码语言:javascript
复制
/**
 * Removes all of the elements from this list.
 * The list will be empty after this call returns.
 */
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; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;
    size = 0;
    modCount++;
}

查看元素(indexOf / lastIndexOf)

和ArrayList集合一样,indexOf作用就是为了返回元素首次出现的位置

而lastIndexOf就是返回元素最后出现的位置

代码语言:javascript
复制
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;
}

/**
 * Returns the index of the last occurrence of the specified element
 * in this list, or -1 if this list does not contain the element.
 * More formally, returns the highest index {@code i} such that
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
 * or -1 if there is no such index.
 *
 * @param o element to search for
 * @return the index of the last occurrence of the specified element in
 *         this list, or -1 if this list does not contain the element
 */
public int lastIndexOf(Object o) {
    int index = size;
    if (o == null) {
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (x.item == null)
                return index;
        }
    } else {
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (o.equals(x.item))
                return index;
        }
    }
    return -1;
}

Queue<Integer> queue = new LinkedList<>();

使用接口 对象名 = new 类名; 方式实例化的对象只能调用接口中有的方法,而不能调用类中特有的方法。

而使用类名 对象名 = new 类名;方式创建出来的对象可以调用所有的方法

对于LinkedList() 它同样可以实现队列

方法有

  • peek() —– 返回队列的头部
  • element() —–返回队列的头部【 此方法与 peek 的不同之处仅在于,如果此队列为空,它会抛出异常。】
  • poll() ——出列
  • offer() —– 入列【使用容量受限队列时,此方法通常更可取, add后者只能通过引发异常来插入元素。】
  • add() ——入列
  • remove() —– 删除队列的头部
代码语言:javascript
复制
public E peek() {	
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

/**
 * Retrieves, but does not remove, the head (first element) of this list.
 *
 * @return the head of this list
 * @throws NoSuchElementException if this list is empty
 * @since 1.5
 检索但不删除此队列的头部。 此方法与 peek 的不同之处仅在于,如果此队列为空,它会抛出异常。
 */
public E element() {
    return getFirst();
}

/**
 * Retrieves and removes the head (first element) of this list.
 *
 * @return the head of this list, or {@code null} if this list is empty
 * @since 1.5
 */
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

/**
 * Retrieves and removes the head (first element) of this list.
 *
 * @return the head of this list
 * @throws NoSuchElementException if this list is empty
 * @since 1.5
 检索并删除此队列的头部,如果此队列为空,则返回 null 。
返回值:
此队列的头部,或者 null 如果此队列为空
 */
public E remove() {
    return removeFirst();
}

/**
 * Adds the specified element as the tail (last element) of this list.
 *
 * @param e the element to add
 * @return {@code true} (as specified by {@link Queue#offer})
 * @since 1.5
 */
public boolean offer(E e) {
    return add(e);
}

Deque双端队列

它在queue的基础上又增加了特殊情况,也就是返回值

此接口定义访问双端元素的方法。提供了插入、删除和检查元素的方法。这些方法中的每一个都以两种形式存在:一种在操作失败时引发异常另一种返回特殊值( null 或 false,具体取决于操作)。后一种形式的插入操作专门设计用于容量受限 Deque 的实现;在大多数实现中,插入操作不会失败。

详见源码

代码语言:javascript
复制
/**
    * Inserts the specified element at the front of this list.
    *
    * @param e the element to insert
    * @return {@code true} (as specified by {@link Deque#offerFirst})
    * @since 1.6
    */
   public boolean offerFirst(E e) {
       addFirst(e);
       return true;
   }

   /**
    * Inserts the specified element at the end of this list.
    *
    * @param e the element to insert
    * @return {@code true} (as specified by {@link Deque#offerLast})
    * @since 1.6
    */
   public boolean offerLast(E e) {
       addLast(e);
       return true;
   }

   /**
    * Retrieves, but does not remove, the first element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the first element of this list, or {@code null}
    *         if this list is empty
    * @since 1.6
    */
   public E peekFirst() {
       final Node<E> f = first;
       return (f == null) ? null : f.item;
    }

   /**
    * Retrieves, but does not remove, the last element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the last element of this list, or {@code null}
    *         if this list is empty
    * @since 1.6
    */
   public E peekLast() {
       final Node<E> l = last;
       return (l == null) ? null : l.item;
   }

   /**
    * Retrieves and removes the first element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the first element of this list, or {@code null} if
    *     this list is empty
    * @since 1.6
    */
   public E pollFirst() {
       final Node<E> f = first;
       return (f == null) ? null : unlinkFirst(f);
   }

   /**
    * Retrieves and removes the last element of this list,
    * or returns {@code null} if this list is empty.
    *
    * @return the last element of this list, or {@code null} if
    *     this list is empty
    * @since 1.6
    */
   public E pollLast() {
       final Node<E> l = last;
       return (l == null) ? null : unlinkLast(l);
   }

   /**
    * Pushes an element onto the stack represented by this list.  In other
    * words, inserts the element at the front of this list.
    *
    * <p>This method is equivalent to {@link #addFirst}.
    *
    * @param e the element to push
    * @since 1.6
    */
   public void push(E e) {
       addFirst(e);
   }

   /**
    * Pops an element from the stack represented by this list.  In other
    * words, removes and returns the first element of this list.
    *
    * <p>This method is equivalent to {@link #removeFirst()}.
    *
    * @return the element at the front of this list (which is the top
    *         of the stack represented by this list)
    * @throws NoSuchElementException if this list is empty
    * @since 1.6
    */
   public E pop() {
       return removeFirst();
   }

   /**
    * Removes the first occurrence of the specified element in this
    * list (when traversing the list from head to tail).  If the list
    * does not contain the element, it is unchanged.
    *
    * @param o element to be removed from this list, if present
    * @return {@code true} if the list contained the specified element
    * @since 1.6
    */
   public boolean removeFirstOccurrence(Object o) {
       return remove(o);
   }

   /**
    * Removes the last occurrence of the specified element in this
    * list (when traversing the list from head to tail).  If the list
    * does not contain the element, it is unchanged.
    *
    * @param o element to be removed from this list, if present
    * @return {@code true} if the list contained the specified element
    * @since 1.6
    */
   public boolean removeLastOccurrence(Object o) {
       if (o == null) {
           for (Node<E> x = last; x != null; x = x.prev) {
               if (x.item == null) {
                   unlink(x);
                   return true;
               }
           }
       } else {
           for (Node<E> x = last; x != null; x = x.prev) {
               if (o.equals(x.item)) {
                   unlink(x);
                   return true;
               }
           }
       }
       return false;

参考来源: https://pdai.tech/md/java/collection/java-collection-Queue&Stack.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Collection
    • ArrayList源码剖析
      • 三个构造器
      • add方法
      • Set方法
      • get方法
      • remove方法
      • trimToSize()方法
      • indexOf()—获取元素第一次出现的位置
      • lastIndexOf() — 获取元素最后一次出现的位置
    • LinkedList源码剖析
      • 三个构造器
      • getFirst() 获取第一个元素
      • getLast() 获取最后一个元素:
      • 删除元素(如下)
      • removeFirst(), removeLast(), remove(e), remove(index)
      • add方法
      • addAll()
      • Set方法
      • get方法
      • Clear()
      • 查看元素(indexOf / lastIndexOf)
      • Queue<Integer> queue = new LinkedList<>();
      • Deque双端队列
    • 参考来源: https://pdai.tech/md/java/collection/java-collection-Queue&Stack.html
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档