集合简单介绍
集合的由来
数组长度是固定,当添加的元素超过了数组的长度时需要对数组重新定义,太麻烦,java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加,随着元素的减少而减少
数组和集合的区别
区别一:
数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
集合只能存储引用数据类型(对象)集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象
区别二:
数组长度是固定的,不能自动增长
集合的长度的是可变的,可以根据元素的增加而增长
数组和集合什么时候用
如果元素个数是固定的推荐用数组
如果元素个数不是固定的推荐用集合
集合继承简单体系图
迭代器
概述
集合是用来存储元素,存储的元素需要查看,那么就需要迭代(遍历)
迭代器的使用
Collection<String> collection = new ArrayList<>();
collection.add("a");
collection.add("b");
collection.add("c");
collection.add("d");
Iterator<String> iterator = collection.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
由上面的代码可知,Iterator为迭代器类,而查看API可得知,集合类中并没有实现Iterator这个接口,却实现了Iterable接口
迭代器原理:
迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么就需要在每一个类中定义hasNext()和next()方法,这样做是可以的,但是会让整个集合体系过于臃肿,迭代器是将这样的方法向上抽取出接口,然后在每个类的内部,定义自己迭代方式,这样做的好处有二,第一规定了整个集合体系的遍历方式都是hasNext()和next()方法,第二,代码有底层内部实现,使用者不用管怎么实现的,会用即可
那Iterator和Iterable有些什么区别?
1.Iterator是迭代器类,而Iterable是为了只要实现该接口就可以使用foreach,进行迭代.
2.Iterable中封装了Iterator接口,只要实现了Iterable接口的类,就可以使用Iterator迭代器了。
那为什么这些集合类不直接实现Iterator呢?
因为Iterator接口的核心方法next()或者hasNext() 是依赖于迭代器的当前迭代位置的。
如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。
除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。
但即时这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。
多个迭代器是互不干扰的。 此段文字转载至:http://liuyun025.iteye.com/blog/1321045
ArrayList迭代器简单源码分析(JDK1.8)
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return 当前指针
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;//当前指针的大小和集合中元素的个数进行比较
}
@SuppressWarnings("unchecked")
public E 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;//指针往后移动一位
return (E) elementData[lastRet = i];//返回元素
}
public void 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();
}
}
}
并发修改异常
上面提到了并发修改异常,什么是并发修改异常呢?
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
list.add("e");
}
}
异常信息
a
Exception in thread "main" java.util.ConcurrentModificationException
....
某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。
解决方案
1.迭代器迭代元素,迭代器修改元素(ListIterator的特有功能add)
2.集合遍历元素,集合修改元素( toArray() )
下面就演示下第一种解决方案
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
ListIterator<String> iterator = list.listIterator();
while(iterator.hasNext()) {
String str = iterator.next();
if(str.equals("c")) {
iterator.add("e");
}
}
System.out.println(list);
}
正常输出
[a, b, c, e, d]