本章会用大量代码做具体演示。统一说明:返回类型E在JDK1.5之前是Object,1.5之后等泛型再讲。
先介绍一个偶然发现的快捷键和一个很实用的快捷键:
Alt + Shift + N 快速调用创建菜单。直接按开头的首字母进行创建即可,某些时候感觉比Ctrl + N更快捷。
Alt+Shift+R 选中变量后,可集体改名。
目录
7、案例演示:Collection存储自定义对象并用迭代器遍历(while实现+for实现)
3、案例演示:List集合存储学生对象并遍历(通过size()和get()方法结合使用遍历)。
1、案例演示:需求:我有一个集合,请问,我想判断里面有没有"world"这个元素。如果有,我就添加一个"javaee"元素,请写代码实现。
4、Vector和ArrayList、LinkedList三者之间的区别,共同点。
集合概述
1、数组中存储的不是对象,而是存储记录对象的地址值。
2、数组和集合存储引用数据类型,存的都是地址值。
3、集合的由来:
数组长度是固定的,当添加的元素超过了数组的长度时,需要对数组重新定义,太麻烦。java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加,随着元素的减少而减少。
4、数组和集合的区别:
区别1(存储类型) :
数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值。
集合只能存储引用数据类型(对象),集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象。
100→new Integer(100)
区别2(存储长度):
数组长度是固定的,不能自动增长。
集合的长度的是可变的,可以根据元素的增加而增长。
5、数组和集合什么时候用:
如果元素个数是固定的推荐用数组。效率比较高,比较节省内存。集合的长度是一点点增长上去的,会把原来的对象变成垃圾。
如果元素个数不是固定的推荐用集合。
6、集合体系图:
Collection接口
1、接口 Collection<E>的概述:
public interface Collection<E> extends Iterable<E>,Collection层次结构中的根接口。Collection表示一组对象,这些对象也称为collection的元素。一些collection允许有重复的元素,而另一些则不允许。一些collection是有序的,而另一些则是无序的。
JDK不提供此接口的任何直接实现:它提供更多具体的子接口(如Set和List)实现。
java.util包下,使用需要导包。接口不能直接实例化。
<>是泛型,先不讲,暂时不加。
2、Collection<E>接口方法:
boolean add(E e) 确保此collection包含指定的元素(可选操作)。E代表Object,所以集合中可以添加任何对象。 返回全是true,因为子类Set集合中无序不能存储相同的元素,那时会返回false,而List集合是有序可以存储两个相同的元素,则返回true。
ArrayList的爷爷类AbstractCollection重写了toString方法:(源代码)
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext()) //如果集合中没有元素,就返回空的中括号。
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('['); //添加]
for (;;) { //无限循环
E e = it.next(); //拿到每一个元素
sb.append(e == this ? "(this Collection)" : e); //添加元素
if (! it.hasNext())
return sb.append(']').toString(); //最后添加],再转换成字符串返回
sb.append(',').append(' '); //中间用,和空格隔开
}
}
注:如果打印对象的引用出现的结果和Object类里的toString()结果不一样,就说明重写了toString()方法,如果该类中找不到toString()方法,就去他父类中找或去他爷爷类中找。
boolean remove(Object o) 从此collection中删除指定元素的单个实例,如果存在的话(可选操作)。 已知返回值都是true,所以直接Collection变量调用方法即可。
void clear() 移除此collection中的所有元素(可选操作)。
boolean contains(Object o) 如果此collection包含指定的元素,则返回 true 。boolean类型直接打印即可。
boolean isEmpty() 如果此collection不包含元素,则返回 true 。
int size() 返回此collection中的元素个数。
3、消除非提示未使用变量的黄色波浪线。
新出现的黄色波浪线是因为没加泛型,有一定的安全隐患。
如果不希望看到,光标放上去Ctrl+1,选中@ Add @SuppressWarming 'rawtypes' to 'main()'这个注解。选中后会在main方法上方出现@SuppressWarnings({ "rawtypes", "unchecked"})我们将它们两个挪到整个类上去,这样所有方法多都会有这养的控制,“原始类型”,“不检查”。在之后的演示代码中会看到。
Collection生成的注解是原始类型,Collection类型变量.add(参数);生成的注解是不检查。
4、集合的遍历之集合转数组遍历:
集合的遍历:其实就是依次获取集合中的每一个元素。
把集合转换成数组,可以实现集合的遍历 Object[] arr = Collection变量名.toArray();
Collection<E>接口方法:
Object[] toArray() 返回包含此collection中所有元素的数组。
演示:
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Student("张三",23)); //Object obj = new Student("张三",23);
coll.add(new Student("李四",24));
coll.add(new Student("王五",25));
coll.add(new Student("赵六",26));
Object[] arr = coll.toArray(); //将集合转换成数组。
for (int i = 0; i < arr.length; i++) {
Student s = (Student)arr[i]; //向下转型成Student。
System.out.println(s.getName() + "," + s.getAge());
}
}
解析Collection coll = new ArrayList();
父类引用指向子类对象,所以Collection变量名.add(new 目标类对象); 的参数在添加的时候,目标类对象已经自动提升为Object类对象。
括号内相当于Object obj = new 目标类对象;所以转换成数组的时候全部转换成了Object数组。虽然存储着目标类对象,但已经提升为Object类型。
多态的弊端:不能使用子类特有的属性和功能,需要向下转型。
5、Collection集合所有带All功能的演示:
boolean addAll(Collection c) 将指定collection中的所有元素都添加到此collection中(可选操作)。如果错用了add(Collection c) 将指定collection看成一个对象(单个元素)添加到此集合中。
boolean removeAll(Collection c) 删除指定集合中包含的所有此集合的元素(可选操作)。 删除的是交集。
boolean containsAll(Collection c) 如果此集合包含指定 集合中的所有元素,则返回true。即使仅指定集合或仅此集合某元素重复,但各存在要素都包含即返回true。
boolean retainAll(Collection c) 仅保留此集合中包含在指定集合中的元素(可选操作)。取交集,如果调用的集合改变就返回true,如果调用的集合不变就返回false。
解析:即使目标集合包含调用集合所有元素,返回值也是false。即目标集合包含此集合时,返回false。此集合真包含目标集合时,返回true。即使目标集合无与此集合相同的元素,变量中存储值变成空中括号,返回值为true。
总结:当集合变量中元素在调用此方法后,不发生任何变化,则retain()返回为true;变量元素减少,则返回为false。
Iterator接口+迭代器
1、迭代器概述:
集合是用来存储元素,存储的元素需要查看,那么就需要迭代(遍历) 。迭代就是遍历,便利就是迭代。
2、Collection<E>接口方法:
Iterator<E> iterator() 返回在此collection的元素上进行迭代的迭代器。(获取迭代器)
3、Iterator<E>接口概述:
public interface Iterator<E> 对collection进行迭代的迭代器。java.util包下,使用需要导包。
4、迭代器与枚举有两点不同:
ⅰ.迭代器允许调用者利用定义良好的语义在迭代期间从迭代器指向的集合移除元素。
ⅱ.方法名称得到了改进。
5、Iterator接口方法:
boolean hasNext() 如果仍有元素可以迭代,则返回true。
E next() 返回迭代的下一个元素。
void remove() 从迭代器指向的collection中移除迭代器返回的最后一个元素(可选操作)。
6、案例演示:迭代器的使用:
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
//对集合中的元素迭代(遍历)
/*Iterator it = c.iterator(); //获取迭代器
boolean b1 = it.hasNext(); //判断集合是否有元素,有就返回true
Object obj1 = it.next();
System.out.println(b1);
System.out.println(obj1);
Object obj2 = it.next();
System.out.println(it.hasNext());
System.out.println(obj2);*/
Iterator it = c.iterator();
while(it.hasNext()) { //利用while循环实现上述操作
System.out.println(it.next());
}
}
7、案例演示:Collection存储自定义对象并用迭代器遍历(while实现+for实现)
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import com.bean.Student;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo6_Iterator {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Student("张三",23));
c.add(new Student("李四",24));
c.add(new Student("王五",25));
c.add(new Student("赵六",26));
//仅使用for的判断功能,达到和while实现相同的功能,即用迭代器对自定义对象进行遍历
for(Iterator it = c.iterator(); it.hasNext(); ) {
Student s = (Student)it.next(); //向下转型
System.out.println(s.getName() + "," + s.getAge()); //获取对象中的姓名和年龄
}
System.out.println("------------------------------");
Iterator it = c.iterator(); //获取迭代器
while(it.hasNext()) { //判断集合中是否有元素
//System.out.println(((Student)(it.next())).getName() + "," + ((Student)(it.next())).getAge());
Student s = (Student)it.next(); //向下转型
System.out.println(s.getName() + "," + s.getAge()); //获取对象中的姓名和年龄
}
}
}
8、迭代器原理:
迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样的,那么就需要在每一个类中都定义方法hasNext()和next()。虽然这样做是可以的,但是会让整个集合体系过于臃肿。迭代器则是将这样的方法向上抽取出接口,然后在每个类的内部,定义了自己的迭代方式。
这样做的好处有二:
第一,规定了整个集合体系的遍历方式都是hasNext()和next()方法。
第二,代码由底层内部实现,使用者不用管怎么实现的,会用即可。
9、迭代器源码解析:
1,在eclipse中ctrl + shift + t找到ArrayList类。
2,ctrl+O查找iterator()方法。
3,查看返回值类型是new Itr(),说明Itr这个类实现Iterator接口。
4,查找Itr这个内部类,发现重写了Iterator中的所有抽象方法 。
源代码:
public Iterator<E> iterator() {
return new Itr(); //Itr应该是Iterator的子类,这里返回的是Iterator的子类对象。因为不能new一个Iterator返回,只能new一个Iterator的子类对象。
}
private class Itr implements Iterator<E> { //Itr实现Iterator,是Iterator子类,把Iterator所有的方法进行了重写。所以真正调用的是Itr中的hasNext()和Next()。
int cursor; // index of next element to return //cursor指针,初始化为0。
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() { //重写了hasNext() 判断是否有元素
return cursor != size; //判断元素个数size是否等于0,不等于则返回true。证明调用集合是有元素的。
}
@SuppressWarnings("unchecked")
public E next() { //重写了next() 获取元素,并把指针向后加一。
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; //每调用一次next(),cursor向后加一一次,指针向后移动一次。
return (E) elementData[lastRet = i];
}
public void remove() { //重写了remove()
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
List接口
1、接口List<E>集合的概述:
public interface LIst<E> extends Collection<E>,有序集合(也称为序列 )。
此接口的用户可以精确控制列表中每个元素的插入位置。 用户可以根据元素整数索引(列表中的位置)访问元素,并搜索列表中的元素。
与Set不同,列表通常允许重复的元素。更确切的讲,列表通常允许满足e1.equals(e2)的元素对e1和e2,并且如果列表半身允许null元素的话,通常它们允许多个null元素。
java.util包,使用需要导包。Collection的子接口。同样是接口不能new,找他的子类ArrayList。
2、List集合的特有功能概述:
void add(int index, E element) 将指定的元素插入此列表中的指定位置(可选操作)。
add功能演示:
import java.util.ArrayList; import java.util.List; @SuppressWarnings({ "rawtypes", "unchecked" }) public class Demo1_List { public static void main(String[] args) { List list = new ArrayList(); //父类引用指向子类对象,镇长开发的时候,直接ArrayList list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add(4, "f"); //index <= size && index >= 0都不会报异常。允许在屁股后面加元素。 // list.add(1, "e"); //在索引为1的位置上插入元素"e" // list.add(10, "z"); //IndexOutOfBoundsException索引越界异常,当存储是使用不存在的索引就会出现索引越界异常 System.out.println(list); } }
E remove(int index) 删除该列表中指定位置的元素(可选操作)。
使用:
Object obj = list.remove(索引);通过索引删除元素,将被删除的元素返回。
一个问题:
public static void main(String[] args) { List list = new ArrayList(); list.add(111); list.add(222); list.add(333); list.remove(111); //删除的时候不会自动装箱,不会删除一个new Integer对象。只要给整数都被当做索引。 System.out.println(list); //IndexOutOfBoundsException索引越界异常,他把111当成索引了。 }
E get(int index) 返回此列表中指定位置的元素。Object obj = list.get(索引); 通过该方法遍历集合中所有元素,属于List集合特有的方式,Set集合没有索引。
演示:通过索引遍历List集合:
public static void main(String[] args) { List list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }
E set(int index, E element) 用指定的元素(可选操作)替换指定位置的元素。
3、案例演示:List集合存储学生对象并遍历(通过size()和get()方法结合使用遍历)。
import java.util.ArrayList;
import java.util.List;
import com.bean.Student;
/**
* 向List集合中存储学生对象。
* 通过size()和get()方法结合使用遍历。
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo2_List {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Student("张三", 23)); //Object obj = new Student("张三" ,18);
list.add(new Student("李四", 24));
list.add(new Student("王五", 25));
list.add(new Student("赵六", 26));
for(int i = 0; i < list.size(); i++) {
Student s = (Student)list.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
}
并发修改异常
1、案例演示:需求:我有一个集合,请问,我想判断里面有没有"world"这个元素。如果有,我就添加一个"javaee"元素,请写代码实现。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* 需求:我有一个集合,请问,我想判断里面有没有"world"这个元素。如果有,我就添加一个"javaee"元素,请写代码实现。
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo3_List {
public static void main(String[] args) {
List list = new ArrayList(); //父类引用指向子类对象
list.add("a"); //Object obj = new String("a");自动类型提升
list.add("b");
list.add("world");
list.add("c");
list.add("d");
list.add("e");
/*Iterator it = list.iterator(); //获取迭代器
while(it.hasNext()) { //判断集合中是否有元素
String str = (String)it.next(); //向下转型
if(str.equals("world")) {
list.add("javaee"); //这里会抛出ConcurrentModificationException并发修改异常.集合遍历的同时在增加元素,叫做并发修改。
}
}*/
ListIterator lit = list.listIterator(); //获取迭代器(List集合特有的)如果想在遍历的过程中添加元素,可以用ListIterator中的add方法。
while(lit.hasNext()) {
String str = (String)lit.next();
if(str.equals("world")) {
// list.add("javaee"); //ConcurrentModificationException
lit.add("javaee");
}
}
System.out.println(list);
}
}
2、并发修改异常概述:
public class ConcurrentModificationException extends RuntimeException,当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。java.util包下。
3、解决方案:
ⅰ.迭代器迭代元素,迭代器修改元素(通过ListIterator的特有功能add)。集合不动。
ⅱ.集合遍历元素,集合修改元素。
ListIterator接口
1、List<E>接口方法:
ListIterator<E> listIterator() 返回此列表元素的列表迭代器(按适当顺序)。
2、接口ListIterator<E>概述:
public interface ListIterator<E> extends Iterator<E> 系列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。java.util包下。
3、接口ListIterator<E>方法:
void add(E e) 将指定的元素插入列表(可选操作)。
boolean hasNext() 以正向遍历列表时,如果列表迭代器有多个元素,则返回true(换句话说,如果next返回一个元素而不是抛出异常,则返回true)。翻译:是否有下一个。
E next() 返回列表中的下一个元素。翻译:返回下一个元素。
boolean hasPrevious() 如果以逆向遍历列表,列表迭代器有多个元素,则返回true。翻译:是否有前一个。
E previous() 返回列表中的前一个元素。翻译:返回上一个元素。
4、演示:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo4_ListIterator {
public static void main(String[] args) {
List list = new ArrayList(); //父类引用指向子类对象
list.add("a"); //Object obj = new String("a");自动类型提升
list.add("b");
list.add("world");
list.add("c");
list.add("d");
list.add("e");
ListIterator lit =list.listIterator(); //获取迭代器
while(lit.hasNext()) {
System.out.println(lit.next()); //获取元素并将指针向后移动
}
System.out.println("------------------");
while(lit.hasPrevious()) { //如果指针指向0,直接返回false。所以需要先正着遍历,再反着遍历。
System.out.println(lit.previous()); //获取元素并将指针向前移动
}
}
}
Vector类
1、Vector类概述:
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serialisable,Vector类可以实现可增长的对象数组。
与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector的大小可以根据需要增大缩小,以适应创建Vector后进行添加或移除项的操作。
从父类继承了Iterator的方法。
2、Vector类特有功能:
public void addElement(E obj) 将指定的组件添加到此向量的末尾,将其大小增加1。 Element元素。
public E elementAt(int index) 返回指定索引处的组件。
public Enumeration elements() 返回此向量的组件的枚举。
3、接口Enumeration<E> 概述:
public interface Enumeration<E> ,实现Enumeration接口的对象,它生成一系列元素,一次生成一个。连续调用nextElement方法将返回一系列的连续元素。
注:此接口的功能与Iterator接口的功能是重复的。此外,Iterator接口添加了一个可选的移除操作,并使用较短的方法名。新的实现应该优先考虑使用Iterator接口而不是Enumeration接口。
5、接口Enumeration方法:
boolean hasMoreElements() 测试此枚举是否包含更多的元素。相当于Iterator中的hasNext()
E nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此美剧的下一个元素。相当与Iterator中的next()。
6、案例演示 :Vector的迭代:
import java.util.Enumeration;
import java.util.Vector;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo5_Vector {
public static void main(String[] args) {
Vector v = new Vector(); //创建集合对象,List的子类
v.addElement("a");
v.addElement("b");
v.addElement("c");
v.addElement("d");
//Vector迭代
Enumeration en = v.elements(); //获取枚举
while(en.hasMoreElements()) { //判断集合中是否有元素
System.out.println(en.nextElement());//获取集合中的元素
}
}
}
List的三个子类
1、数据结构之数组、链表特点:
数据结构之数组: 查询快,修改也快,增删慢。数据结构之链表:查询慢,修改也慢,增删快。
2、List的三个子类的特点:
ArrayList:底层数据结构是数组,查询快,增删慢。线程不安全,效率高。
Vector:底层数据结构是数组,查询快,增删慢。线程安全,效率低。
Vector相对ArrayList查询慢(线程安全的)。
Vector相对LinkedList增删慢(数组结构)。
LinkedList:底层数据结构是链表,查询慢,增删快。线程不安全,效率高。
3、LinkedList底层链表实现的代码:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //当索引小于集合长度的一半
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; //集合从头向尾遍历
return x;
}else { //当索引大于集合长度的一半
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev; //集合从尾向头遍历
return x;
}
}
4、Vector和ArrayList、LinkedList三者之间的区别,共同点。
Vector和ArrayList的区别:Vector是线程安全的,效率低。ArrayList是线程不安全的,效率高。
Vector和ArrayList的共同点:都是数组实现的。
ArrayList和LinkedList的区别:ArrayList底层是数组结构的,查询和修改快。LinkedList底层是链表结构的,增和删比较快,查询和修改比较慢。
ArrayList和LinkedList的共同点:都是线程不安全的。
5、List有三个儿子,我们到底使用谁呢?
查询多用ArrayList,增删多用LinkedList,如果都多ArrayList。Vector只在面试的时候用,已经被ArrayList替代了,之后会学个工具类,其中有个方法能使线程不安全的集合,变成线程安全的。默认用ArrayList。