List接口和ArrayList类
目录
数组和List
没有比较就没有伤害
在集合类中,List是最基础的一种集合:它是一种有序链表。
List的行为和数组几乎完全相同:List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,List的索引和数组一样,从0开始。
数组和List类似,也是有序结构,如果我们使用数组,在添加和删除元素的时候,会非常不方便。例如,从一个已有的数组{'A', 'B', 'C', 'D', 'E'}中删除索引为2的元素:
┌───┬───┬───┬───┬───┬───┐ │ A │ B │ C │ D │ E │ │ └───┴───┴───┴───┴───┴───┘ │ │ ┌───┘ │ │ ┌───┘ │ │ ▼ ▼ ┌───┬───┬───┬───┬───┬───┐ │ A │ B │ D │ E │ │ │ └───┴───┴───┴───┴───┴───┘
这个“删除”操作实际上是把'C'后面的元素依次往前挪一个位置,而“添加”操作实际上是把指定位置以后的元素都依次向后挪一个位置,腾出来的位置给新加的元素。这两种操作,用数组实现非常麻烦。
因此,在实际应用中,需要增删元素的有序列表,我们使用最多的是ArrayList。实际上,ArrayList在内部使用了数组来存储所有元素。例如,一个ArrayList拥有5个元素,实际数组大小为6(即有一个空位):
size=5
┌───┬───┬───┬───┬───┬───┐ │ A │ B │ C │ D │ E │ │ └───┴───┴───┴───┴───┴───┘
当添加一个元素并指定索引到ArrayList时,ArrayList自动移动需要移动的元素:
size=5
┌───┬───┬───┬───┬───┬───┐ │ A │ B │ │ C │ D │ E │ └───┴───┴───┴───┴───┴───┘
然后,往内部指定索引的数组位置添加一个元素,然后把size加1:
size=6
┌───┬───┬───┬───┬───┬───┐ │ A │ B │ F │ C │ D │ E │ └───┴───┴───┴───┴───┴───┘
继续添加元素,但是数组已满,没有空闲位置的时候,ArrayList先创建一个更大的新数组,然后把旧数组的所有元素复制到新数组,紧接着用新数组取代旧数组:
size=6
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │ A │ B │ F │ C │ D │ E │ │ │ │ │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
现在,新数组就有了空位,可以继续添加一个元素到数组末尾,同时size加1:
size=7
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │ A │ B │ F │ C │ D │ E │ G │ │ │ │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
可见,ArrayList把添加和删除的操作封装起来,让我们操作List类似于操作数组,却不用关心内部元素如何移动。
List主要接口方法
我们考察List<E>接口,可以看到几个主要的接口方法:
- 在末尾添加一个元素:void add(E e)
- 在指定索引添加一个元素:void add(int index, E e)
- 删除指定索引的元素:int remove(int index)
- 删除某个元素:int remove(Object e)
- 获取指定索引的元素:E get(int index)
- 获取链表大小(包含元素的个数):int size()
ArrrayList类
ArrayList类支持可随需要而增长的动态数组。ArrayList类对于使用索引取出元素有较高的效率,它可以使用索引来快速定位对象。但元素做删除或插入速度较慢,因为使用了数组,需要移动后面的元素已调整索引顺序。
┌───┬───┬───┬───┬───┬───┐ │ A │ B │ C │ D │ E │ │ └───┴───┴───┴───┴───┴───┘
因为它是动态数组,初始化大小容量4,当数据存满时扩容是以当前数组容量大小的2倍扩容,之后再把数组元素一个一个的存入,数组在扩容时浪费一定的内存空间,和存储时间,而且,元素添加是一个装箱的过程,所以说,跟一般的数组比起来,速度上差些。
LinkedList类
但是,实现List接口并非只能通过数组(即ArrayList的实现方式)来实现,另一种LinkedList通过“链表”也实现了List接口。在LinkedList中,它的内部每个元素都指向下一个元素:
┌───┬───┐ ┌───┬───┐ ┌───┬───┐ ┌───┬───┐ HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │ │ └───┴───┘ └───┴───┘ └───┴───┘ └───┴───┘
我们来比较一下ArrayList和LinkedList:
应用:ArrayList 和LinkedList
public class Main { List<String> lis = new LinkedList<>(); /*添加元素*/ lis.add("tony"); lis.add("tom"); lis.add("jack"); System.out.println("<List原列表元素>:"); for(int i=0;i<lis.size();i++){ String result = (String)lis.get(i); System.out.println(result); } /*删除元素*/ lis.remove(1); /*修改元素*/ lis.set(1, "lucy"); System.out.println("<删除元素后的List列表>:"); for (int i = 0; i < lis.size(); i++) { String result = (String) lis.get(i); System.out.println(result); } System.out.println("---------------------------------"); /*查找元素*/ if(lis.contains("vivian")){ System.out.println("包含此元素vivian!"); } else{ System.out.println("vivian该元素不存在!"); } }
练一练:集合的增删查改
练习(暂时不看)
用ArrayList集合重写Java任务:图书的接口处理里BookDataSet类的成员变量和方法。
答案:
package entity; import java.util.*; public class BookDataSet { /*有商品的品种数,如num=5,代表目前经营5个品种的商品 */ private int num=0; /*存放的商品*/ private ArrayList<Goods> goods = new ArrayList<>(); public BookDataSet(){ } public BookDataSet(int num,ArrayList<Goods> goods){ this.num =num; this.goods=goods; } public void getGoods(){ System.out.println("站点商品明细:"); //显示所有书籍 for(int i=0;i<goods.size();i++) { System.out.println(goods.get(i).toString()); } } public Goods getGoods(String id){ // 遍历所有的书籍信息 for(int i=0;i<this.num;i++){ if (this.goods.get(i).cid.equals(id)) { return this.goods.get(i); } } // 没有找到任何书籍信息抛出null return null; } public void addGoods(Goods mygoods){ this.goods.add(mygoods); num += 1; } public void delGoods(Goods myGoods){ //将数组中myGoods条目对应的商品数量减为0 this.goods.remove(myGoods); } public void ModifyGoods(Goods myGoods){ //将数组中myGoods条目对应的信息修改一下 for(int i=0;i<goods.size();i++){ if (this.goods.get(i).cid==myGoods.cid) { this.goods.set(i, myGoods); } } } @Override public String toString() { int i; String s=""; for(i=0;i<this.num;i++){ s +=goods.get(i).toString()+"\n"; } return s; } }
返回 Java程序设计