【学习笔记】集合(二)
List 集合
-
List 是 Collection 的子接口
-
特点:有序、有下标、元素可以重复
-
它除了Collection 中的方法外,还有一些自己特有的方法
-
void add(int index,Object o) //在index位置插入对象o
-
boolean addAll(int index,Collection c) //将一个集合中的元素添加到此集合的index位置
-
Object get(int index) //返回集合中指定位置的元素
-
List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素
-
listIterator() //方法更多的迭代器
-
listIterator(int index) //从集合的指定位置开始遍历
-
boolean remove(int index) //移除指定位置的元素
-
boolean remove(Object o) //从此列表移除第一次出现的指定元素
-
添加元素,把元素添加到指定位置,以及删除某个位置的元素
package com.collection.listDemo;
import java.util.ArrayList;
import java.util.List;
public class Demo01 {
public static void main(String[] args) {
//创建集合
List list = new ArrayList();
//添加元素,把元素添加到指定位置
list.add("iphone");
list.add("小米");
list.add(0,"华为");
System.out.println(list);
list.remove(1);
System.out.println(list);
}
}
遍历:由于集合有下标,所以可以使用for循环来遍历集合,也可以使用 listIterator 来遍历集合
-
listIterator 中特有的方法
-
add(E e) //插入元素
-
hasPrevious() //如果以逆向遍历集合,迭代器中有多个元素,就返回true
-
nextIndex() //返回对应元素的下标
-
previous() //返回列表中前一个元素
-
previousIndex() //逆向遍历时,返回对应元素下标
-
set(E e) //用指定元素替换所遍历的元素
-
package com.collection.listDemo;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo01 {
public static void main(String[] args) {
//创建集合
List list = new ArrayList();
//添加元素,把元素添加到指定位置
list.add("iphone");
list.add("小米");
list.add(0,"华为");
System.out.println(list);
//使用listIterator 遍历集合
System.out.println("正向");
ListIterator it = list.listIterator();
while (it.hasNext()){
System.out.println(it.nextIndex()+":"+it.next());
}
System.out.println("逆向");
while (it.hasPrevious()){
System.out.println(it.previousIndex()+":"+ it.previous());
}
}
}
注意:在使用逆序遍历之前,必须先正序遍历,否则会没有内容
原因是,在listIerator初始时,有一个指针默认是在集合的第一位,这时候如果再去逆向遍历,指针就会向前走,就不会去遍历数组
判断:和Collection 一样 有 contain() 和 isEmpty() 两个方法,使用方法也相同
获取位置:在list 中独有的方法 indexOf() 它可以获取元素在集合中的位置
package com.collection.listDemo;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo01 {
public static void main(String[] args) {
//创建集合
List list = new ArrayList();
//添加元素,把元素添加到指定位置
list.add("iphone");
list.add("小米");
list.add(0,"华为");
System.out.println(list);
//获取位置
System.out.println("华为在集合中的位置:"+list.indexOf("华为"));
}
}
list 在保存 数字数据时:
-
保存数字数据时,会有自动装箱的操作,因为集合中只能保存引用数据类型,保存不了基本数据类型
-
在进行删除数字数据时,如果你直接往remove方法中传入这个数字时,如果这个数字超过了集合的下标时,就会报下标越界异常,就算没有超出集合的下标,那么删除的哪个数据,也有可能不是你想要删除的数据,而是数字对应的下标的数据。
-
这个问题我们有两种方法解决
-
强制类型转换成Object
-
new Integer(数字)
-
-
package com.collection.listDemo;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo01 {
public static void main(String[] args) {
//创建集合
List list = new ArrayList();
//添加数字元素
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
System.out.println("集合中元素的个数:"+list.size());
System.out.println(list);
list.remove((Object) 20);
System.out.println("集合中元素的个数:"+list.size());
System.out.println(list);
list.remove(new Integer(30));
System.out.println("集合中元素的个数:"+list.size());
System.out.println(list);
}
}
补充方法:subList(int fromIndex,int toIndex) 返回子集合,下标从fromIndex到toIndex 含头不含尾
package com.collection.listDemo;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo01 {
public static void main(String[] args) {
//创建集合
List list = new ArrayList();
//添加数字元素
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
System.out.println("集合中元素的个数:"+list.size());
System.out.println(list);
List list1 = list.subList(2,4);
System.out.println(list1);
}
}
List 实现类
-
ArrayList:
-
数组结构实现,查询快、增删慢
-
JDK1.2版本加入,运行效率快、线程不安全
-
-
Vector:
-
数组结构实现,查询快、增删慢
-
JDK1.0版本加入,运行效率慢、线程安全
-
-
LinkedList:
-
链表结构实现,增删快,查询慢
-
ArrayList 方法
-
ArrayList的增加、删除、迭代、判断、查找与List相同
我们下面看一下在remove 和 contains 里面直接new 一个对象
package com.collection.listDemo.arrayList;
import com.collection.collectionDemo.Student;
import java.util.ArrayList;
public class Demo01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
Student s1 = new Student("张三",22);
Student s2 = new Student("李四",23);
Student s3 = new Student("王五",20);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.remove(new Student("张三",22));
System.out.println("元素的个数:" + arrayList.size());
System.out.println(arrayList);
}
}
我们发现,新new的对象没有被删除,尽管它的属性和s1相同,我们如果想要将属性重复的对象在集合中删除,就需要重写equals方法,去判断两个对象的属性是否相等。
在执行上面那段代码,就会发现属性相同的对象被删除了
contain() 方法与之相同
ArrayList 源码分析
-
常量:
-
DEFAULT_CAPACITY = 10; //默认容量 为10
-
elementData 存放元素的数组
-
size 实际的元素个数,一开始一定小于默认容量
-
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
-
private static final Object[] EMPTY_ELEMENTDATA = {};
-
-
构造方法(无参):
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
先把空的数组赋值给存放数据的数组 elementData
所以没有向集合中添加任何元素时,数组容量为0
-
add() 方法 //添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
这时候size是0,我们传入的参数是1
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
这个方法先调用calculateCapacity,传入的参数是,存放元素的数组elementData(此时这个数组为空)、add方法传入的参数 1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
这个方法先判断elementData是否与DEFAULTCAPACITY_EMPTY_ELEMENTDATA 相等,(第一次调用肯定相等,因为在构造方法中就把DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给了elementData),如果相等,就返回默认容量和传入参数中大的那一个,(第一次调用,默认容量为10,是大的那个数),如果不相等就返回传入参数
上面的方法ensureCapacityInternal 接收到值之后,调用ensureExplicitCapacity
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
如果传入的参数大于elementData的长度就调用grow方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
第一次调用:oldCapacity = 0,newCapacity = 0,minCapacity = 10
newCapacity - minCapacity 肯定小于0,所以 newCapacity 被赋值为minCapacity =10
但是newCapacity - MAX_ARRAY_SIZE(int 类型能表示的最大值) 肯定小于0
然后给把newCapacity 的长度复制给 elementData 数组
所以第一次向集合中插入数据时,数组的长度为10,size = 1
然后我们看第二次插入数据时:
-
add 方法中 size = 1;然后传入的参数为 2
-
calculateCapacity 方法中 elementData 不等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以返回的是 minCapacity 也就是 2
-
ensureExplicitCapacity 方法中,minCapacity - elementData.length = -8,不大于0,所以不用调用grow()
当数组存满时,也就是达到了默认容量的时候,就需要扩容了
-
add 方法中 size = 10;然后传入的参数为 11
-
calculateCapacity 方法中 elementData 不等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以返回的是 minCapacity 也就是 11
-
ensureExplicitCapacity 方法中,minCapacity - elementData.length = 11-10,大于零调用grow
-
grow 方法中,oldCapacity = 10,newCapacity = 15,直接把newCapacity = 15 复制给elementData,elementData的长度也就变成了15
Vector 方法
vector 中的方法与Arraylist 中的方法基本相同,不同的是在遍历集合时,vector可以使用枚举器来遍历
package com.collection.listDemo.vector;
import java.util.Enumeration;
import java.util.Vector;
public class Demo01 {
public static void main(String[] args) {
//创建集合
Vector v = new Vector();
//添加元素
v.add("张三");
v.add("李四");
v.add("王五");
v.add("赵六");
//遍历集合
Enumeration en = v.elements();
while (en.hasMoreElements()){
String str = (String) en.nextElement();
System.out.println(str);
}
}
}
-
其他方法:
-
vector.firstElement //获取第一个元素
-
vector.lastElement //获取最后一个元素
-
vector.elementAt //获取某个位置的元素
-
package com.collection.listDemo.vector;
import java.util.Enumeration;
import java.util.Vector;
public class Demo01 {
public static void main(String[] args) {
//创建集合
Vector v = new Vector();
//添加元素
v.add("张三");
v.add("李四");
v.add("王五");
v.add("赵六");
//遍历集合
System.out.println(v.firstElement());
System.out.println(v.lastElement());
System.out.println(v.elementAt(2));
}
}
LinkedList 方法
-
创建集合,添加、删除、遍历元素,判断,获取
package com.collection.listDemo.linkList;
import com.collection.collectionDemo.Student;
import java.util.*;
public class Demo01 {
public static void main(String[] args) {
//创建集合
LinkedList link = new LinkedList();
//创建学生对象
Student s1 = new Student("张三",15);
Student s2 = new Student("李四",18);
Student s3 = new Student("王五",12);
//把学生对象添加到集合中
link.add(s1);
link.add(s2);
link.add(s3);
System.out.println(link);
//删除元素
// link.remove(s1);
// link.remove(1);
// link.remove(new Student("李四",18));
// System.out.println(link);
// link.clear();
//遍历集合
//1.for循环
System.out.println("-------for循环---------");
for (int i = 0; i < link.size(); i++) {
System.out.println(link.get(i));
}
//2.forEach循环
System.out.println("-------forEach循环---------");
for (Object o:
link) {
System.out.println(o);
}
//3.Iterator迭代器
System.out.println("-------Iterator迭代器---------");
Iterator it = link.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4.listIterator迭代器
System.out.println("-------listIterator迭代器---------");
ListIterator lit = link.listIterator();
System.out.println("正向");
while (lit.hasNext()){
System.out.println(lit.nextIndex()+":"+lit.next());
}
System.out.println("逆向");
while(lit.hasPrevious()){
System.out.println(lit.previousIndex()+":"+lit.previous());
}
//判断
System.out.println("是否含有对象s2:"+link.contains(s2));
System.out.println("集合是否为空:"+link.isEmpty());
//获取
System.out.println("s1在集合中的位置:"+link.indexOf(s1));
}
}
LinkList 源码
-
int size = 0; //集合的大小
-
Node first //链表的头节点
-
Node last //链表的尾节点
-
Node 类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
item: 是当前的元素
next :下一个节点
prev: 前一个结点
-
add() 方法
public boolean add(E e) {
linkLast(e);
return true;
}
add方法 调用 linkLast 方法 并且把要添加的元素传进去
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
第一次调用:
-
把last 赋值给 l ,这时 l 为null,然后创建一个节点,这个节点的next 和 prev 都为 null,因为只有它一个节点。
-
然后把newNode 赋值给 last ,也就是last指向新创建的节点newNode
-
判断 l 是否为空,为空的话,就把first 也指向 新节点 newNode
第二次调用:
-
把last 赋值给 l ,这个时候 l 不为空,l 是我们第一次创建的节点,创建第二个节点,第二个节点的next 为空,但prev 为 l,也就是第一个节点
-
然后把第二个节点赋值给last,也就是last指向第二个节点
-
判断 l 是否为空,这时候 l 不为空,l 为第一个节点,所以我们让第一个节点的next 指向第二个节点
删除元素时,就是改变节点之间的指向,没有位置的移动,所以比较快
ArrayList 和 LinkList 的区别
-
ArrayList:必须开辟连续空间,查询快,增删慢
-