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) -
在实际开发时要避免扩容,因为扩容的底层其实是创建一个更大的数组将老数组替换:
- 创建一个更大的数组对象
- 将老数组里面的元素赋值到新数组里面
- 改变引用指向
- 回收老数组对象
- 继续添加元素
-
如何手动扩/缩容?
- 扩容: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
}
}