您好,登錄后才能下訂單哦!
今天小編給大家分享一下Java集合框架概覽之ArrayList源碼分析的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
下面是一段簡單的集合操作代碼,實例化一個 ArrayList 集合并插入和獲取元素的代碼:
public static void main(String[] args) { // 實例化一個初始容量為5的 ArrayList 集合 List list = new ArrayList<String>(6); // 向指定索引位置插入數據 list.add(1, "hello");// 代碼行號:17 // 獲取指定索引位置的數據 System.out.println(list.get(1)); }
小伙伴可以先思考一下執行的結果是什么?
好啦,揭曉謎底:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:665)
at java.util.ArrayList.add(ArrayList.java:477)
at com.example.qgdemo.studydemo.Test.Test2.main(Test2.java:17)
細心的小伙伴已經注意到了上面的那段代碼有一行專門標注了行號,而執行的結果的異常行號剛好是我標注的那一行,不難得出 就是在:list.add(1, "hello");
這一行就拋出了異常。那么問題到底出現在哪里了呢?
下面我們從這短短幾行代碼逐行深入源碼去刨析,挖出隱藏寶藏。
ArrayList的初始化
先從集合的初始化入手:
List list = new ArrayList<String>(5);
上源碼(硬菜):
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Constructs an empty list with the specified initial capacity. * 根據指定的初始化容量構造一個空的 list 集合 * @param initialCapacity the initial capacity of the list 初始化的容量 * @throws IllegalArgumentException if the specified initial capacity * is negative 如果指定的容量為負數則拋出異常 */ 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); } }
簡單分析一下這段源碼:
ArrayList 底層采用普通的數組來存儲數據,通過 elementData
這一成員變量來存儲集合的數據。
在實例化是當傳入的參數大于零,則實例化一個對應容量的 Object 數組并賦值給我們的 elementData
成員變量。
在實例化是當傳入的參數等于零,安裝默認的 EMPTY_ELEMENTDATA
空數組賦值給elementData 成員變量。
在實例化是當傳入的參數小于零,則拋出指定的 IllegalArgumentException 異常信息。
小貼士: 細心的小伙伴會注意到我們的 elementData
成員變量使用了 transient
關鍵字修飾,這里簡單科普一下:
被 transient 修飾的變量不能被序列化。
transient 只能作用于實現了 Serializable 接口的類當中。
transient 只能用來修飾普通成員變量字段。
分析到這里目前沒有發現關于我們的問題的信息,我們繼續往下看。
ArrayList添加元素
現在到了我們的重頭戲,從執行結果反饋來看,拋出異常的位置就在這:list.add(1, "hello");
,讓我們磨刀霍霍向源碼一探究竟。
/** * 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). * 向 ArrayList 中指定的位置插入指定的元素,如果當前位置已經有元素,則會將該位置之后的所有元素統一往后移一位。 * @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++; // 集合的實際大小累加 } /** * The size of the ArrayList (the number of elements it contains). * ArrayList 的成員變量,保存了已有元素的數量 * @serial */ private int size; /** * A version of rangeCheck used by add and addAll. */ private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
源碼刨析:
首先看rangeCheckForAdd(index);
這一行,這個方法主要是檢查待插入的 index 索引是否越界或者非法。 經過縝密分析(debug ),發現正是這里的檢查拋出的異常,導致我們出師未捷身先死/(ㄒoㄒ)/~~,第一步就被絆倒了。
既然判斷的是 index 和 size 的大小,那么我們回過頭看一下:private int size;
這個玩意,通過其注釋我們得知這個成員變量保存了已有元素的數量,那么問題就很明顯了:我們初始化后的集合雖然已經有了一個指定容量的數組,但是并沒有實際元素,所以 size 依然為0。不難得出結論:==這種指定位置插入元素的方法必須從下標0開始順次插入元素,你敢隔空插入它就敢死給你看!==
好了,雖然問題的根源找到了,但是源碼我們還是要繼續往下看的。
// 判斷是否需要擴容 private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 修改次數累加 // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * 增加集合的容量以確保容納至少 * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);// 將原容量擴容至原來的1.5倍,以本例來說就是擴容至:6+3=9 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //取 newCapacity 和 minCapacity 的最大值賦值給 newCapacity,考慮了溢出的情況 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } /** * The maximum size of array to allocate. 定義了數組允許分配的最大長度 * Some VMs reserve some header words in an array. 一些虛擬機在數列中會保留一些頭部信息(需要預留一定容量) * Attempts to allocate larger arrays may result in 嘗試取分配更長的數組可能會導致內存溢出 * OutOfMemoryError: Requested array size exceeds VM limit :申請的數組長度超過了虛擬機的限制 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow 溢出檢查 throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? // 如果申請的最小容量比數組的容量上限還大則容量設置為: Integer.MAX_VALUE : // Integer.MAX_VALUE,否則設置為:數組容量上限(MAX_ARRAY_SIZE) MAX_ARRAY_SIZE; }
源碼刨析:
這一方法:private void ensureCapacityInternal(int minCapacity)
主要是為了檢查集合是否可以滿足指定的最小數量的元素的要求。
如果滿足的話,則只需要將修改次數:modCount++
累加就完事。
如果容量不夠用了,則需要進行擴容,那么就需要調用 grow(int minCapacity)
方法來執行擴容任務。
通過 int newCapacity = oldCapacity + (oldCapacity >> 1);
方法將原來的容量擴容1.5倍,后續的兩個 if 判斷考慮了 newCapacity 溢出的情況,最終保證了 newCapacity 必然為正數。
小貼士: 上面的:grow(int minCapacity)
方法用到了移位運算符。 java中有三種移位運算符: <<
:左移運算符,num << 1,相當于num乘以2。 >>
:右移運算符,num >> 1,相當于num除以2。 >>>
:無符號右移,忽略符號位,空位都以0補齊。
確定了集合的新容量,接下來就需要將集合的舊數據拷貝到新數組當中:
System.arraycopy(elementData, index, elementData, index + 1, size - index); //[#System] 調用了系統級的數組拷貝方法 /** * @param src the source array. 源數組 * @param srcPos starting position in the source array. 源數組的起始下標 * @param dest the destination array. 目標數組 * @param destPos starting position in the destination data. 目標數組的起始下標 * @param length the number of array elements to be copied. 需要拷貝的元素數量 * */ public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
源碼刨析:
這塊內容沒啥好說的,無非就是調用系統函數進行新舊數據的拷貝。
主要看看上面數組拷貝方法的注釋,做一個大致的了解。
ArrayList 由于是基于數組來存儲數據的,所以支持按指定下標來獲取數據:
/** * Returns the element at the specified position in this list. * 返回集合指定位置的元素 * @param index index of the element to return 要返回的元素下標 * @return the element at the specified position in this list 指定下標的元素 * @throws IndexOutOfBoundsException {@inheritDoc} 下標越界異常 */ public E get(int index) { rangeCheck(index); // 索引越界檢查 return elementData(index); // 按下標獲取元素 } /** * Checks if the given index is in range. If not, throws an appropriate * runtime exception. This method does *not* check if the index is * negative: It is always used immediately prior to an array access, * which throws an ArrayIndexOutOfBoundsException if index is negative. */ private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
源碼刨析:
這塊內容主要也就進行了一次下標越界的檢查,檢查通過就直接返回數據。
ArrayList 主要提供了:指定下標刪除,按元素刪除,批量刪除,特定條件刪除,下標區間刪除等方法。
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * 刪除特定位置的集合元素,將該元素之后的所有元素往前挪一位。 * @param index the index of the element to be removed 待刪除的元素下標 * @return the element that was removed from the list 返回已刪除的元素 * @throws IndexOutOfBoundsException {@inheritDoc} 下標越界異常 */ 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; // clear to let GC do its work 將數組末尾空缺出來的位置引用置為null,便于GC return oldValue; }
源碼刨析:
首先就是進行下標越界的檢查,然后就是修改次數的累加以及獲取待刪除的舊數據。
這塊:int numMoved = size - index - 1;
主要是計算一下當前待刪除元素之后有多少需要移動的元素數量。
這個 numMoved
的值可能為 0 ,比如說當前集合就一個元素,在刪除下標為 0 的時候,numMoved
的值就為 0 ,所以接下來做了一次 if 是否大于零的判斷,如果為 true,則執行數組的拷貝,將需要處理的元素全部往前移動一位。
上一步移動完元素之后,數組的最后一個位置就空缺出來了,然后就通過 elementData[--size] = null;
將該位置的引用置為 null 便于GC處理。
/** * Removes the first occurrence of the specified element from this list, * 如果集合中指定的元素存在的話,刪除首次出現的那個指定元素。 * if it is present. If the list does not contain the element, it is * 如果指定元素不存在,則不會有什么影響。 * unchanged. More formally, removes the element with the lowest index * <tt>i</tt> such that * 一般情況下,會刪除下標最小的那個元素 * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> * (if such an element exists). Returns <tt>true</tt> if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). * * @param o element to be removed from this list, if present 待刪除的元素 * @return <tt>true</tt> if this list contained the specified element 如果元素存在則返回true,反之為false */ public boolean remove(Object o) { if (o == null) { // 如果待刪除的元素為 null,則直接遍歷數組元素和 null 進行匹配 for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); // 執行刪除操作 return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { // 遍歷匹配所有元素 fastRemove(index);// 執行刪除操作 return true; } } return false; } /* * Private remove method that skips bounds checking and does not * return the value removed. */ private void fastRemove(int index) { modCount++; // 修改次數累加 int numMoved = size - index - 1;// 計算需要移動的元素數量,指的就是當前刪除位置之后的元素數量 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);// 重新進行數據拷貝 elementData[--size] = null; // clear to let GC do its work 將數組末尾空缺出來的位置引用置為null,便于GC }
源碼刨析:
首先根據待刪除的元素是否為 null
進行分別處理。
如果待刪除的元素是 null
的話,則遍歷所有數組元素和 null
進行匹配,匹配到第一個的話則執行刪除,直接返回true。
如果不是,則遍歷所有數組元素和待刪除元素進行 equals
匹配,匹配到第一個的話則執行刪除,直接返回true。
對于 fastRemove
這個方法的話,筆者認為在上一個指定下標刪除的時候可以直接調用這個方法的。
小貼士: 由上述源碼可以得出一個結論:==如果你想刪除一個 ArrayList
中的 所有 null
元素,調用一次 remove(null);
是無法刪除全部的null元素的。==
/** * Removes from this list all of its elements that are contained in the * specified collection. * 按給定的特定元素集合去刪除當前集合的匹配元素。 * @param c collection containing elements to be removed from this list 包含待刪除元素的刪除 * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list * is incompatible with the specified collection * (<a href="Collection.html#optional-restrictions" rel="external nofollow" rel="external nofollow" >optional</a>) * @throws NullPointerException if this list contains a null element and the * specified collection does not permit null elements * (<a href="Collection.html#optional-restrictions" rel="external nofollow" rel="external nofollow" >optional</a>), * or if the specified collection is null * @see Collection#contains(Object) */ public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); // 對 c 集合進行空判斷 return batchRemove(c, false); // 執行批量刪除 } // 批量刪除方法 private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; // 操作結果標識 try { // 通過遍歷原集合,將不符合刪除條件的 [r] 位置的元素替換掉 [w] 位置的元素,并將 [w] 累加。 for (; r < size; r++) // 每次循環 r++ if (c.contains(elementData[r]) == complement) // 如果待刪除的集合不包含有原集合的元素 elementData[w++] = elementData[r]; // 則用原集合當前下標位置 [r] 的元素覆蓋掉下標位置為 [w] 的元素 // 并將 [w] 累加。 } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. // 只有在拋出異常時:r != size ,那么此時需要將未比對的元素拼接在已經處理過的元素后面 if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; // 重新設置 [w] 的值,因為在下一步會將 [w] 之后的元素設置為null,此時的 [r] 為拋出異常位置 } // 在極端情況下,如果待刪除集合和原集合的元素完全無交集,則 `w == size`,這種情況下無需對原集合進行任何操作。 if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; // 將 [w] 之后的元素全部賦值為 null。 modCount += size - w; // 對 modCount 進行重新設置 size = w; modified = true; } } return modified; } // 集合是否包含指定的元素 public boolean contains(Object o) { return indexOf(o) >= 0; // 遍歷集合查找指定元素 }
源碼刨析:
首先對傳入的參數集合進行 null
判斷,如果為空則直接拋出異常。
接下來就是通過 batchRemove
方法執行批量刪除。
這個:batchRemove
方法首先會遍歷原集合,使用其 [r] 位置元素去匹配在待刪除集合是否存在:
如果不存在,說明該元素并不是要刪除元素,則:if (c.contains(elementData[r]) == complement)
返回true,此時的原集合的 [r] 位置元素需要覆蓋到原集合的 [w] 位置,此時 [w] 進行累加,方便下次進行覆蓋。
在循環完畢后,最終的結果就是:原集合的 [w] 位置之前的元素都是需要保留下來的。
在 finally
代碼塊中:進行的第一個 if (r != size)
判斷是為了在出現異常時(此時r != size
)單獨將后續未處理完的 [r] 之后數據拷貝到原集合的 [r] 之后,保證數據的完整性。另外還需要重新計算 [w] 的值。
在這一步:if (w != size)
判斷是為了將 [w] 之后的重疊需要刪除的數據賦值為 null,最后修改 modCount 和集合的大小 size 的值。
總之按博主的理解,這個batchRemove
方法總體思想是:將原數組中不匹配的元素通過替換的方式往前聚集,處理到最后那么后面的那部分元素就可以廢棄掉了。
// 根據指定的過濾器判斷匹配的元素是否在集合內:Predicate 接口主要用來判斷一個參數是否符合要求。 public boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); // 指定的過濾器的非null判斷 // figure out which elements are to be removed 找出要刪除的元素 // any exception thrown from the filter predicate at this stage 在這階段拋出的任何異常都不會使得集合發生改變 // will leave the collection unmodified int removeCount = 0; // 刪除數目 final BitSet removeSet = new BitSet(size); // BitSet是一種特殊類型的數組來保存位值,其中數組大小會隨需要增加 final int expectedModCount = modCount; // 記錄修改次數 final int size = this.size; // 這個循環主要是為了記錄需要刪除的元素數目 for (int i=0; modCount == expectedModCount && i < size; i++) { @SuppressWarnings("unchecked") final E element = (E) elementData[i]; if (filter.test(element)) { removeSet.set(i); // 通過 removeSet 來記錄需要刪除的集合下標 removeCount++; // 刪除數目進行累加 } } if (modCount != expectedModCount) { // 正常情況下這兩個值應該是相等的,不相等說明有了并發修改,則拋出異常 throw new ConcurrentModificationException(); } // shift surviving elements left over the spaces left by removed elements // 通過遍歷使用未刪除的元素替換已刪除元素,[i] 代表未刪除的元素下標,[j] 代表被替換的元素下標 final boolean anyToRemove = removeCount > 0; if (anyToRemove) { final int newSize = size - removeCount; // 記錄刪除后的新的容量 for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { i = removeSet.nextClearBit(i); // 找出未刪除元素的下標 [i] elementData[j] = elementData[i]; // 使用未刪除的元素 [i] 替換對應位置 [j] 的元素 } for (int k=newSize; k < size; k++) { // 將下標從 [k] 到之后的位置的元素賦值為null elementData[k] = null; // Let gc do its work } this.size = newSize; if (modCount != expectedModCount) { // 出現并發修改時拋出異常 throw new ConcurrentModificationException(); } modCount++; // 修改次數累加 } return anyToRemove; }
源碼刨析:
這個方法 removeIf
支持指定一個過濾器(Predicate)來刪除指定的若干元素。
在這個方法中用到了:final BitSet removeSet = new BitSet(size);
BitSet 是一種特殊類型的數組,它只能記錄兩種狀態:0
和1
,可以用來代表有沒有
,是與否
等數據。在這塊是為了存儲需要刪除
的元素的下標。
這里使用一個和集合的 size
一樣容量的 BitSet 來對應每一個集合元素的下標,方便后續處理。
通過for (int i=0; modCount == expectedModCount && i < size; i++)
這個循環匹配集合中需要刪除的元素,用其下標來為 removeSet
對應位置 [i] 的狀態位的值為true
。
到了if (modCount != expectedModCount)
這一步就是常規的并發修改的檢查,如果出現并發修改則直接拋出異常。
在 anyToRemove
的值大于零,也就是匹配到有需要刪除的元素,則開始執行數據的刪除操作。
通過 for (int i=0, j=0; (i < size) && (j < newSize); i++, j++)
這個循環來處理,利用BitSet的i = removeSet.nextClearBit(i);
獲取得到下一個值為false
的下標值,也就是獲取未被標記刪除的元素下標。
進行數據替換:elementData[j] = elementData[i];
,上一步已經獲取到未被標記刪除的元素下標 [i],在這一步就可以順次替換掉 [j] 位置的元素,這樣一來就能保證未被標記刪除的元素最終都集中在集合前面連續部分的位置
,也就是在下標 [newSize]
之前。
通過這部分:for (int k=newSize; k < size; k++)
進行遍歷 [k] 之后的元素,執行 elementData[k] = null;
把下標 [k] 以及之后的位置元素賦值為null,便于下一次的 GC 。
最后再次進行并發修改的檢查以及集合的修改次數的累加。
小貼士: 對于這個方法使用這里給一個簡單的例子: 假設有一個字符串集合 list:["Google","Runoob","Taobao","Facebook"],我們想刪除所有帶有 ”oo“的元素; 則可以:list.removeIf(e -> e.contains("oo"));
,最終的集合就變為:["Taobao"]。
/** * Removes from this list all of the elements whose index is between * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. * 刪除下標區間 [fromIndex]至[toIndex]之間的元素,包含[fromIndex] 但是不包含 [toIndex] 。 * Shifts any succeeding elements to the left (reduces their index). * 將刪除區間之后的集合元素統一左移動。 * This call shortens the list by {@code (toIndex - fromIndex)} elements. * (If {@code toIndex==fromIndex}, this operation has no effect.) * 如果[fromIndex]和[toIndex]相等,則對集合無影響。 * @throws IndexOutOfBoundsException if {@code fromIndex} or * {@code toIndex} is out of range * ({@code fromIndex < 0 || * fromIndex >= size() || * toIndex > size() || * toIndex < fromIndex}) */ protected void removeRange(int fromIndex, int toIndex) { modCount++; // 修改次數累加 int numMoved = size - toIndex; // 需要移動的元素數量,也就是待刪除區間的末尾之后的元素數量。 System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // 用刪除區間的末尾之后的元素拷貝至刪除區間的元素進行覆蓋。 // clear to let GC do its work int newSize = size - (toIndex-fromIndex); // 計算新的集合的長度,方便后續進行多余數組元素的置空 for (int i = newSize; i < size; i++) { elementData[i] = null; // 將后半部分的多余位置的元素賦值為 null,方便GC。 } size = newSize; // 重新設置集合的大小 }
源碼刨析:
這個方法可以指定刪除一段下標區間的所有元素。
首先進行修改次數的累加:modCount++;
,然后計算刪除區間的結束下標 [toIndex]
之后的元素數量,也就是 numMoved
,這部分元素需要往前移動來覆蓋待刪除區間的位置。
以數組拷貝的方式:System.arraycopy
將后半部分有效元素往前移動覆蓋掉刪除區間的位置。
重新計算集合的大小:int newSize = size - (toIndex-fromIndex)
,方便后續將后半部分空缺的位置的數據進行無效化。
通過循環遍歷將后部分無效
的集合元素進行賦值 null
的操作,方便 GC 回收。
重新設置集合的大小:size = newSize;
以上就是“Java集合框架概覽之ArrayList源碼分析”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。