# 源码分析篇--Java集合操作(8)根据源码设计一个顺序表

2.6.6 get(int index)方法
我们知道,顺序表中的get方法通过元素的索引获取底层数组的元素。我们可以把该方法理解为顺序表的查询操作,该方法返回元素范型类型,源代码如下所示:

public E get(int index) {
	//范围检查
rangeCheck(index);
return elementData(index);
}

rangeCheck(index);方法如下所示:

private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

outOfBoundsMsg()方法用于异常处理信息,如下所示:

private String outOfBoundsMsg(int index) {
      return "Index: "+index+", Size: "+size;
}

通过源代码我们发现,get方法通过size来控制传入的索引大小,如果索引太大或太小,那么就抛出异常。因此要实现get方法,就必须遵循一个规则,需要对传入的索引的大小进行控制,太大或太小都不行,对于”异常”的索引,可以手动抛出异常。

2.6.7 实现 get(int index)方法

/***
 * @functionName:get
 * @description:通过索引获得数序表的元素
 * @param index
 * @author yzh
 * @date 2018-12-31
 * @return
 */
@SuppressWarnings("unchecked")
public E get(int index){
	//对太大或太小的索引使用异常抛出进行控制,对于顺序表而言,底层数组的有效索引区间为0-size
	if((myElementData == MYDEFAULT_ELEMENTDATA ) || ((size > 0) && index >= size) || index < 0){
		throw new RuntimeException("illegal element index of myElementData");
	}
	return (E) myElementData[index];
}

2.6.8 实现update()方法
对于更新方法,需要传入两个参数,一个是新元素,一个是需要替换的旧元素的位置,代码如下所示:

/***
 * @functionName:update
 * @description:通过索引修改顺序表的元素
 * @param newElement:目标元素
 * @param fromIndex:被修改元素的索引
 * @author yzh
 * @date 2019-01-01
 * @return
 */
public void update(E newElement,int fromIndex){
	//如果等于0,那么说明没有一个元素,或者元素索引过大
	if((myElementData == MYDEFAULT_ELEMENTDATA ) || ((size > 0) && (fromIndex >= size)) || fromIndex < 0){
		throw new RuntimeException("illegal element index of myElementData");
	}
	myElementData[fromIndex] = newElement;
}

当然,我们还可以通过注入新元素对旧元素进行修改,其思路是先获取到旧元素的索引,然后通过该索引注入新元素,来达到修改元素的目的:

/***
 * @functionName:update
 * @description:通过新元素修改顺序表的元素
 * @param oldElement:旧元素
 * @param newElement:目标元素
 * @author yzh
 * @date 2019-01-01
 * @return
 */
public void update(E oldElement,E newElement){
	//返回该元素在该数组中第一次出现时的索引
	int oldElementIndex = indexOf(oldElement);
	//如果oldElementIndex存在于myElementData数组中,那么返回值必然不小于0
	if(oldElementIndex >= 0){
		myElementData[oldElementIndex] = newElement;
	}
}

/***
 * @functionName:indexOf
 * @description:通过顺序表的元素获得第一个索引
 * @param element:元素
 * @author yzh
 * @date 2019-01-01
 * @return
 */
public int indexOf(E element) {
	if (element == null) {
		throw new RuntimeException("param is not null!");
	}else{
		for (int i = 0; i < size; i++) {
			//比的是同一个对象
			if (element.equals(myElementData[i])) {
				return i;
			}else{
				continue;
			}
		}
		return -1;
	}
}

测试:

MyArrayList<String> list1 = new MyArrayList<String>();
list1.add("a");
list1.add("b");
list1.add("c");
MyArrayList<String> list2 = new MyArrayList<String>();
list2.add("aa");
list2.add("bb");
list2.add("cc");
MyArrayList<String> list3 = new MyArrayList<String>();
list3.add("aaa");
list3.add("bbb");
list3.add("ccc");
MyArrayList<String> list31 = new MyArrayList<String>();
list31.add("aaa1");
list31.add("bbb1");
list31.add("ccc1");

Object[] obj2 = new Object[]{list1,list2,list3};

MyArrayList<MyArrayList<String>> list4 = new MyArrayList<MyArrayList<String>>();
list4.add(list1);
list4.add(list2);
list4.add(list3);

System.out.println(list4.get(0).equals(obj2[0]) && list4.get(0) == obj2[0]);

System.out.println();
for(int i = 0;i<list4.size();i++){
	for(int j = 0;j<list4.get(i).size();j++){
		System.out.print(list4.get(i).get(j)+" ");
	}
	System.out.println();
}
System.out.println();
//修改元素
list4.update(list3, list31);
for(int i = 0;i<list4.size();i++){
	for(int j = 0;j<list4.get(i).size();j++){
		System.out.print(list4.get(i).get(j)+" ");
	}
	System.out.println();
}
/**
a b c 
aa bb cc 
aaa bbb ccc 

a b c 
aa bb cc 
aaa1 bbb1 ccc1 
*/

2.6.9实现delete()方法

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos,int length);

在实现delete()方法之前,先来复习一下System的arraycopy()方法,这个方法是一个静态本地方法,我们知道,这个方法的是对数组进行拷贝,它有五个参数,如下所示:
第一个参数表述原数组,第二个参数index+1表示从删除元素后的索引开始对原数组进行拷贝,第三个参数表示目标数组,第四个参数表示将原数组拷贝的元素从目标数组的index位置开始进行放置,第五个参数moveCapacityNum表示从原数组拷贝元素的个数:

System.arraycopy(myElementData, index+1, myElementData, index, moveCapacityNum);
例
int[] i = new int[]{1,4,6,7,9};
int[] j = new int[8];
System.arraycopy(i,2,j,0,i.length-2);
for(int e: j){
	System.out.print(e+" ");
}
上面输出的结果为(6 7 9 0 0 0 0 0)

说明,System.arraycopy(i,2,j,0,i.length-2);表示i数组的第3个元素开始拷贝,拷贝到j数组中,从j数组的第一个索引开始存储复制的元素,一共存储来自i数组自第3个元素起的3个元素,但j数组开辟了8个存储内存空间,因此没有存储元素的空位用0补上。

例
下面输出的结果为(1 6 7 9 0)
int[] i = new int[]{1,4,6,7,9};
int len = i.length;
System.arraycopy(i,2,i,1,len-2);
i[--len] = 0;
for(int e : i){
	System.out.print(e+" ");
}

说明:上面的代码表示为从i数组的第3个元素开始复制,一共复制3个元素给i数组,并将复制得到的元素从i数组的第2个存储空间开始存储,然后通过i[–len] = 0;最后将最后一个元素置0。这个例子正是顺序表删除元素的原理。

顺序表的删除元素的源码如下所示:

public E remove(int index) {
  //范围检查
rangeCheck(index);
//记录改变的次数
modCount++;
//获得需要删除的元素
E oldValue = elementData(index);
//移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
//复制数组,因为删除的索引为index的元素,因此index之后的元素都是需要复制的元素,因此第二个参数为index+1, 第四个参数为被删除元素的索引,也就是需要被替换的元素,因此,第五个参数为size-(i+1),即制元素的个数。
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//将最后一个元素置空
elementData[--size] = null;
return oldValue;
}
//根据索引获取元素
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
实现的delete方法如下所示。
/***
 * @functionName:delete
 * @description:通过顺序表的索引删除元素
 * @param index
 * @author yzh
 * @date 2019-01-01
 * @return
 */
public void delete(int index){
	//元素索引过大或过小
	if((myElementData == MYDEFAULT_ELEMENTDATA ) || ((size > 0) && index >= size) || index < 0){
		throw new RuntimeException("illegal element index of myElementData");
	//如果0<=index<size && size>0
	}else{
		//从index索引开始移动元素的个数
		int moveCapacityNum = size-index-1;
		//如果索引在0-size - 2之间
		if(index < (size - 1)){
			/*
			1,2,3,4,5  ==>删除第3个元素 ==>1,2,3,5,这个问题可以转换成:1,2,3,5,5删除最后一个元素,同理:1,2,3,4,5  ==>删除第2个元素 ==>1,3,4,5,这个问题可以转换成:1,3,4,5,5删除最后一个元素,也就是从i数组的第3个元素开始复制,一共复制3个元素给i数组,并将复制得到的元素从i数组的第2个存储空间开始存储,然后将最后一个元素置空;第一个参数表述原数组,第二个参数index+1表示从删除元素后的索引开始对原数组进行拷贝,第三个参数表示目标数组,第四个参数表示将原数组拷贝的元素从目标数组的index位置开始进行放置,第五个参数moveCapacityNum表示从原数组拷贝元素的个数
			*/
			System.arraycopy(myElementData, index+1, myElementData, index, moveCapacityNum);
		}
		//通过上面的arraycopy拷贝之后,多出了一个元素,还有一种情况删除的是最后一个元素即index==size - 1
		myElementData[--size] = null;//相当于myElementData[size-1] = null;size--;
	}
}

测试:

MyArrayList<String> list1 = new MyArrayList<String>();
list1.add("a");
list1.add("b");
list1.add("c");
MyArrayList<String> list2 = new MyArrayList<String>();
list2.add("aa");
list2.add("bb");
list2.add("cc");
MyArrayList<String> list3 = new MyArrayList<String>();
list3.add("aaa");
list3.add("bbb");
list3.add("ccc");
MyArrayList<String> list31 = new MyArrayList<String>();
list31.add("aaa1");
list31.add("bbb1");
list31.add("ccc1");
Object[] obj2 = new Object[]{list1,list2,list3};
MyArrayList<MyArrayList<String>> list4 = new MyArrayList<MyArrayList<String>>();
list4.add(list1);
list4.add(list2);
list4.add(list3);
System.out.println();
for(int i = 0;i<list4.size();i++){
	for(int j = 0;j<list4.get(i).size();j++){
		System.out.print(list4.get(i).get(j)+" ");
	}
	System.out.println();
}
System.out.println();
list4.update(list3, list31);
for(int i = 0;i<list4.size();i++){
	for(int j = 0;j<list4.get(i).size();j++){
		System.out.print(list4.get(i).get(j)+" ");
	}
	System.out.println();
}
//删除元素
list4.delete(1);
System.out.println();
list4.update(list3, list31);
for(int i = 0;i<list4.size();i++){
	for(int j = 0;j<list4.get(i).size();j++){
		System.out.print(list4.get(i).get(j)+" ");
	}
	System.out.println();
}
/**output:
a b c 
aaa1 bbb1 ccc1
*/

下面给出了一个根据元素删除顺序表的元素:

/***
 * @functionName:delete
 * @description:通过顺序表的元素删除元素
 * @param element
 * @author yzh
 * @date 2019-01-01
 * @return
 */
public void delete(E element){
	if (element == null) {
		throw new RuntimeException("param is not null!");
	}else{
		for (int i = 0; i < size; i++) {
			//比的是同一个对象
			if (element.equals(myElementData[i])) {
				delete(i);
			}
		}
	}
}

测试:

list4.delete(list31);
for(int i = 0;i<list4.size();i++){
	for(int j = 0;j<list4.get(i).size();j++){
		System.out.print(list4.get(i).get(j)+" ");
	}
	System.out.println();
}
/**output:
a b c
*/

2.6.10实现clear()方法
顺序表的clear()方法的源码如下所示:

public void clear() {
modCount++;

for (int i = 0; i < size; i++)
//元素置空
elementData[i] = null;
//顺序表的大小置0
size = 0;
}

我实现的clear()方法如下所示:

/***
 * @functionName:clear
 * @description:清空元素
 * @author yzh
 * @date 2019-01-01
 * @return
 */
public void clear(){
	//底层数组的元素置空
	myElementData = MYDEFAULT_ELEMENTDATA;
	//顺序表的大小置0
	size = 0;
}
    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/yzh18373476791/article/details/85577462
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞