文章目录
- 集合
- Java的集合类
- Collection
- iterator方法
- Collection的遍历
集合
在Java中,指的就是存放数据的容器,是一个载体,可以一次容纳多个对象。
解决Bug的两种方法:
-
- 打印
System.out.println();
log.info();
-
- debug
- 检查数据
Java的集合类
Collection:
- 存储一个一个的数据
- 先理解为一个袋子,往里面装数据。有各种各样的子实现。
- Collection是最基本的集合接口,一个 Collection 代表一组Object,即 Collection 的元素
- Java不提供直接实现自Collection的类,只提供继承于的子接口(如List和set)。
Map:
- 存储的是键值对数据
- 存储key-value结构的数据。
- key-value结构:就是可以根据一个key,找到一个对应的value。
- Map 接口存储一组键值对象,提供key(键)到value(值)的映射。
Collection
特点:
- Collection是顶级接口,用描述数据存储的接口
- Collection的一些子实现有序,一些无序
- 有序和无序是指:存储和读取的顺序
- 一些子实现允许存储重复的数据,一些不允许
- 允许重复:
- 不允许重复:
- 一些子实现允许存储null,一些不允许
- 允许存储null:
- 不允许存储null:
API:
- 增删改查方法:
boolean add(E e): // 添加一个元素进入Collection
boolean addAll(Collection<? extends E> c): // 添加一个Collection进目标Collection
boolean remove(Object o):// 删除元素, 只删除第一个出现的(如果存在多个)
boolean removeAll(Collection<?> c):// 删除Collection中的所有存在的元素,会全部删除,如果存在多个
boolean contains(Object o):// 判断是否存在指定元素
boolean containsAll(Collection<?> c):// 判断给定的collection中是否全部存在于目标Collection
boolean retainAll(Collection<?> c):// 将原有collection只保留传入的collection。
- 特殊方法:
void clear(): // 清空collection
boolean equals(Object o) :// 判断是否相等
int hashCode():// 计算hashCode
boolean isEmpty(): // 是否为空
int size():// collection里面的元素个数
如果hashcode()
没有重写,代表地址值,并且equal()
和hashcode()
要一起重写
重写hashcode()
的目的:是为了让hashcode和对象里面的变量有关。
- 方便遍历方法:
Object[] toArray(): // 将collection转成一个数组,方便遍历,(无参)
// 就是把原有的集合copy成了一个数组
<T> T[] toArray(T[] a):// 类似,只是传入了一个数组 (有参)
// 1. 当传入的数组长度小于集合长度 --- 则只会使用类型,直接copy一份
// 2. 当传入的数组长度等于集合长度 --- 使用传入的数组
// 3. 当传入的数组长度大于集合长度 --- 会使用传入的数组,最后一个元素后面一个,会置为null
Iterator<E> iterator():// 返回一个迭代器
eg:
private static void testToArray() {
Collection<String> collection = new ArrayList<>();
collection.add("zs");
collection.add("ls");
collection.add("ww");
// 数组的协变 支持用Object[] ---> 任意类型的数组
Object[] objects = collection.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]);
}
}
toArray的源码(有参构造):
public <T> T[] toArray(T[] a) {
// a是用来存储数据的数组。
if (a.length < size)
// 1. 当数组长度小于集合的长度时候。只使用传入数组的类型
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// 2. 如果大于等于集合的长度时候,则直接使用这个数组
// 直接copy
System.arraycopy(elementData, 0, a, 0, size);
// 3. 并且,如果数组长度大于集合长度。直接把a[size] = null,直接把最后一个元素的后面置为null。
if (a.length > size)
a[size] = null;
return a;
}
直接使用toArray的弊端:
- 耗费时间,例如:要是拷贝100w大小的数据时间太长
- 耗费空间,例如:存储100w数据占太多的空间
- jvm耗时,后续使用完毕还需要gc处理
iterator方法
迭代器(iterator),有时又称光标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。
注意事项:
Iterator
是个接口,接口只定义规范,我们获取到了iterator,就可以使用这个对象对数据进行遍历。- 迭代器相当于只保留了一个标识,可以看成一个搬运工,标识我可以怎么拿到这个数据,不copy数据。所有操作的数据都是针对的原有的Collection。
- 比如Collection底层有的是数组,有的是链表。数组的Iterator里面维护的是下标,链表的Iterator里面维护的是指针。所有的具体实现都交给具体的子类
方法:
boolean hasNext(): // 是否有下一个元素
// 只会返回true false,反复调用,光标不会挪动
E next():// 获取下一个元素
// 挪动光标,返回刚刚扫过的元素
// 如果下一个没有元素,则会报错 NoSuchElementException
void remove():// 删除刚刚遍历过的元素
// 不能直接调用remove,必须要先遍历
// 不可以连续两次调用remove
eg:
private static void testIterator() {
Collection<String> collection = new ArrayList<>();
collection.add("zs");
collection.add("ls");
collection.add("ww");
// 先生成一个迭代器
Iterator<String> iterator = collection.iterator();
// 是否有下一个元素
boolean b = iterator.hasNext();
System.out.println(b); // true
// 返回下一个元素
String next = iterator.next();
System.out.println(next); // zs
// 删除刚刚遍历过的元素
iterator.remove();
System.out.println(collection); // ls ww
}
Q:迭代器是个游标,它遍历的时候,被别的线程,把原集合中加了几个元素,减了几个元素,那这时候这次遍历的意义大吗?
A:
-
JDK
采用了存储一个值的方式,去保证在迭代器使用过程中,原有的集合不被修改(当前线程、其他线程)。 -
在Collection内部,有一个
modCount
,用来标识结构变化的次数(get/contains 这种查询不叫结构变化) -
生成迭代器的时候,存储这个
expectedModCount=modCount
,在调用next
、remove
时候,会检查
使用迭代器过程中,如果原结构发生了变化,会报并发修改异常。 -
所以,不要在迭代器迭代过程中,去修改原集合。要不就是在迭代器生成之前,要不就在迭代器使用完成之后。
-
并发修改异常
eg:
用iterator遍历的例子:
public class Demo3 {
public static void main(String[] args) {
Collection<Student> collection = new ArrayList<>();
collection.add(new Student("zs",17,89,"20210104"));
collection.add(new Student("ww",18,79,"20210105"));
collection.add(new Student("ls",19,99,"20210105"));
collection.add(new Student("ml",17,91,"20210104"));
collection.add(new Student("zq",20,85,"20210104"));
System.out.println("遍历前:" + collection);
Iterator<Student> iterator = collection.iterator();
while(iterator.hasNext()){
if(iterator.next().getAge() == 17){
iterator.remove();
}
}
System.out.println("遍历后:" + collection);
}
Collection的遍历
- 使用无参构造方法
Object[] array = collection.toArray();
for (int i = 0; i < array.length; i++) {
String s = (String) array[i];
System.out.println(s);
}
- 使用有参构造方法
String[] array = collection.toArray(new String[0]);
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
- 使用foreach循环
for (String s : collection) {
System.out.println(s);
}
- 使用迭代器
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}