【第13天】Java集合(二)---手动实现ArrayList及其他List接口实现的集合

版权声明:©2018 本文為博主來秋原創,轉載請註明出處,博主保留追責權利。 https://blog.csdn.net/qq_30257081/article/details/84349036

1 ArrayList(续)

1.1 扩容与缩容

ArrayList list = new ArrayList(50);//开辟一个长度为50的集合
ArrayList list = new ArrayList();//开辟一个默认大小的集合,默认值底层设置为10

不管开辟一个定长还是默认长度的集合,其实都可以装无数个元素,集合可以自动地扩容。

  • 扩容的倍数:
    JDK6.0及以前:x * 3/2 + 1(整数类型,去除结果的小数部分)
    JDK7.0及以后:x + (x >> 1) (—> x * 3/2)

  • 在实际开发时要避免扩容,因为扩容的底层其实是创建一个更大的数组将老数组替换:

    1. 创建一个更大的数组对象
    2. 将老数组里面的元素赋值到新数组里面
    3. 改变引用指向
    4. 回收老数组对象
    5. 继续添加元素
  • 如何手动扩/缩容?

    • 扩容:list.ensureCapacity(int 容量);
    • 缩容:list.trimToSize();//去掉因扩容倍数而产生的空余集合空间,将空间大小缩小为元素个数。
  • 虽然数组自动扩容,但是考虑到效率开发,最好定一个大的空间,避免因自动扩容造成效率降低。

  • 手动扩容比自动扩容效率更高。

1.2 手动实现ArrayList

  • 要手动实现(Customed)ArrayList类需创建的成员(2个成员变量/9个方法):

    • 私有的集合的数组底层实现,用来装元素:private Object[] data;

    • 私有表示集合大小,元素个数:private int size;

    • 有参构造方法接收指定长度创建的集合:public EtoakList(int x){}

    • 无参构造方法接收默认长度创建的集合,默认开辟10块空间:public EtoakList(){}

    • 获取当前集合里元素个数,因不需要setSize(),所以getSize()简化为size():public int size(){}

    • 获取指定下标元素:public Object get(int x){}

    • 添加元素到集合:public void add(Object obj){}

    • 按下标删除元素:public void remove(int x){}

    • 按元素所属类指定的equals()删除obj这样的元素:public void remove(Object obj){}

    • 按元素所属类指定的equals()判断是否存在obj这样的元素:public boolean contains(Object obj){}

    • 重写Object类中的toString()

//无泛型版本(JDK6.0之前)
public class TestCustomedArrayList1{
	public static void main(String[] args){
		
		//测试
		CustomedArrayList1 caList = new CustomedArrayList1();
		caList.add(32);
		caList.add(40);
		caList.add(6);

		System.out.println(caList.size());//--->2
		System.out.println(caList.get(0));//--->45
		System.out.println(caList.contains(6));//--->true

		caList.remove(new Integer(40));
		System.out.println(caList.size());//--->2

		System.out.println(caList);//[32,6]
	}
}

class CustomedArrayList1{

	//集合的数组底层实现,用来装元素
	//为了保证什么数据类型都可以装,所以使用Object[]
	private Object[] data;

	//表示集合大小,元素个数
	private int size;


	//接收指定长度创建的集合
	//CustomedArrayList1 list = new CustomedArrayList1(-45);
	public CustomedArrayList1(int x){//x : 数组空间大小
		if(x < 0){//负数
			System.out.println("参数异常");
		}else{//x >= 0
			//定义新数组,即创建集合
			this.data = new Object[x];
		}
	}

	//接收默认长度创建的集合,默认开辟10块空间
	//CustomedArrayList1 list = new CustomedArrayList1();
	public CustomedArrayList1(){
		//data = new Object[10];
		//直接调用有参构造并传入默认值
		this(10);
	}

	//获取当前集合里元素个数,因不需要setSize(),所以getSize()简化为size()
	//int x = list.size();
	public int size(){
		return size;
	}

	//获取指定下标元素
	//Object obj = list.get(-6);
	public Object get(int x){//x:下标
		if(0 <= x && x < size){//x --> 下标正常
			return data[x];
		}else{//下标超出边界
			return "下标超出边界异常";
		}
	}


	//添加元素到集合
	//list.add(元素);
	public void add(Object obj){

		//向集合中添加元素时,底层其实会把这个元素,置入成员data数组里面
		//添加元素前需数组对象是否已满
		if(data.length == size){
			//如果满了就扩容
			//创建一个新的数组对象
			//JDK6.0及之前 x * 3 / 2 + 1
			Object[] temp = new Object[size * 3 / 2 + 1];
			//将老数组里面的元素赋值到新数组里
			System.arraycopy(data,0,temp,0,size);
			//改变引用指向,新数组赋值给老数组新的地址
			data = temp;
			//gc回收老数组空指向的对象
			//扩容后再添加元素
			data[size] = obj;
			//变量++
			size++;
		}else{
			//如果没满
			//直接添加元素
			data[size] = obj;
			//变量++
			size++;
		}
	}

	//按下标删除元素
	//list.remove(3);
	public void remove(int x){//x:下标
		//从集合里删除下标x对应的元素时
		//就是从数组里删除下标x对应的元素

		System.arraycopy(data, x+1, data, x, size-(x+1));
		
		//集合长度减小
		size--;

		/** 类推得arraycopy参数设置:
			list.remove(1);
			45  66  90  15  15
			45	87	66	90	15 -> ArrayList
			[0]	[1]	[2]	[3]	[4]

			删除下标0	从下标1开始复制4=5-1个元素
			删除下标1	从下标2开始复制3=5-2个元素
			删除小标2	从下标3开始复制2=5-3个元素
			删除下标x	从下标x+1开始复制data.length-(x+1)个元素
		*/
	}


	//按元素所属类指定的equals()删除obj这样的元素
	//list.remove("B");
	public void remove(Object obj){
		//底层使用obj和集合里每个元素equals()

		for(int x = 0;x < size;x++){
			//x -> 下标
			//data[x] -> 元素
			if(obj.equals(data[x])){
				//下标x对应的元素和obj视为相等对象
				//删除下标x对应的元素
				//一个remove方法只能删除一个元素
				remove(x);
				break;
			}
		}
	}

	//按元素所属类指定的equals()判断是否存在obj这样的元素
	//boolean x = list.contains(Object obj)
	public boolean contains(Object obj){
		//底层使用obj和集合里每个元素equals()

		//list -> 张三 李四 王五
		//list.contains("李四")
		for(int x = 0;x < size;x++){
			//x -> 下标
			//data[x] -> 元素
			if(obj.equals(data[x])){
				return true;
			}
		}
		return false;
	}

	@Override
	public String toString(){
		//[元素,元素,元素]

		String str = "[";
		for(int x = 0;x < size;x++){
			//x -> 下标
			//data[x] -> 元素
			str += data[x];//data[x].toString()
			if(x != size-1){
				str += ",";
			}
		}

		//[元素,元素,元素]
		str += "]";
		return str;
	}
}
//泛型版本(JDK7.0之后) ★
public class TestCustomedArrayList2{
	public static void main(String[] args){

		AList<Student> list = new AList<Student>();
		Student stu1 = new Student("张三");
		Student stu2 = new Student("张三");

		list.add(stu1);
		list.remove(stu2);
		System.out.println(list);//--->[]
	}
}

class Student{
	String name;
	public Student(String name){
		this.name = name;
	}

	@Override
	public String toString(){
		return name;
	}

	@Override
	public boolean equals(Object obj){
		if(!(obj instanceof Student))return false;
		if(obj == null )return false;
		if(obj == this) return true;
		return this.name.equals(((Student)obj).name);
	}
}

class CustomedArrayList2<E>{//ArrayList

	private Object[] data;
	private int size;

	public CustomedArrayList2(int x){
		data = new Object[x];
	}

	public CustomedArrayList2(){
		data = new Object[10];
	}

	public int size(){
		return size;
	}

	public Object get(int x){
		return data[x];
	}

	public void add(E obj){
	
		if(data.length == size){

			Object[] temp = new Object[size + (size>>1)];
			System.arraycopy(data,0,temp,0,size);
			data = temp;
		}
		data[size] = obj;
		size++;

	}

	public void remove(int x){

		System.arraycopy(data,x+1,data,x,size-(x+1));
		size--;
	}

	public void remove(Object obj){

		for(int x = 0;x < size;x++){
			if(obj.equals(data[x])){
				remove(x);
				break;
			}
		}
	}

	public boolean contains(Object obj){

		for(int x = 0;x < size;x++){
			if(obj.equals(data[x])){
				return true;
			}
		}
		return false;
	}
	
	@Override
	public String toString(){

		StringBuffer buffer = new StringBuffer("[");

		for(int x = 0;x < size;x++){
			buffer.append(data[x].toString());
			if(x != size-1){
				buffer.append(",");
			}
		}
		buffer.append("]");
		return buffer.toString();
	}
}

2 Vector

       Vector与ArrayList使用方法(语法)相同,只是在概念上有所不同:

  • 同步线程不同
    Vector同一时间允许单个线程进行访问,效率较低,但不会出现并发错误;
    ArrayList同一时间允许多个线程访问,效率较高,但可能出现并发错误。

JDK5.0开始,集合的工具类(Collections)里面提供的一个方法(synchronizedList)可以将线程不安全的ArrayList对象变成线程安全的集合对象,于是Vector渐渐被淘汰。

  • 扩容机制不同
    Vector分构造方法,(Vector(10) -> 2倍扩容 10->20->40->80…;Vector(10,3)->定长扩容 10->13 ->16->19…);
    ArrayList分版本(JDK6.0之前 x * 3 / 2 + 1;JDK7.0之后 x + (x >> 1))。

  • 出现的版本不同
    Vector出现于JDK1.0;
    ArrayList出现于JDK1.2。

3 LinkedList

       LinkedList与ArrayList使用方法(语法)相同,只是在概念上有所不同:

  • ArrayList和LinkedList底层采用的数据结构不同 导致优劣势不同

    • ArrayList:底层基于数组实现,
      优点:数组连续存储,所以方便查找遍历和随机访问。
      缺点:添加删除因为要维护连续(1.创建新数组对象 2.复制老数组元素 3.改变引用指向 4.回收老数组 ),所以增删效率较低。

    • LinkedList:底层基于链表实现,
      优点:链表直接通过改变地址指向进行增删,增删元素效率高
      缺点:每次遍历查找都需要从头找起,随机访问、遍历查找效率低

  • 在开发的时候,尽量避免使用LinkedList的get(int 下标)方法

  • ArrayList更适合访问、读取数据,LinkedList适合增删数据

4 Stack

       Stack与ArrayList使用方法(语法)相同,只是在概念上有所不同:这个集合的意义是用数组模拟栈结构。

import java.util.*;
public class TestStack{
	public static void main(String[] args){

		Stack<Integer> ss = new Stack<>();

		ss.push(666);//从栈顶压入一个元素
		ss.push(777);//从栈顶压入一个元素
		ss.push(888);//从栈顶压入一个元素

		System.out.println(ss.pop());//--->888
		System.out.println(ss.pop());//--->777
		System.out.println(ss.pop());//--->666
	}
}

猜你喜欢

转载自blog.csdn.net/qq_30257081/article/details/84349036