并发修改异常和列表迭代器
作者:互联网
并发修改异常(☆☆)
并发修改异常产生的原因:
"迭代器"在遍历集合的时候 , "集合"对元素进行了增删(改变集合长度)。就会产生并发修改异常:
并发: 一起,多个东西 同时操作一个内容。
解决:(☆☆☆☆)
删除:删除所有的a元素
ArrayList<String> al = new ArrayList<>();
al.add("a");
al.add("a");
al.add("a");
al.add("b");
al.add("a");
al.add("a");
解决方案1:集合自己去遍历,不需要你迭代器参与了。 集合自己遍历的时候,集合自己删除。 (这种方式 只针对 List集合, 其他的集合 set Collection 你都用不了)
1:
for (int i = 0; i < al.size(); i++) {
String s = al.get(i);
if(s.equals("a")){
al.remove(i);
i--;
}
}
2:
for (int i = al.size()-1; i >=0 ; i--) {
String s = al.get(i);
if(s.equals("a")){
al.remove(i);
}
}
解决方案2: 我迭代器遍历集合的时候, 我让迭代器自己去删除元素。
1:适用与 任何 单列集合
Iterator<String> iterator = al.iterator(); //
while (iterator.hasNext()){
String next = iterator.next();
if (next.equals("a")){
iterator.remove();
}
}
2:适用于 list集合。
ListIterator<String> lit = al.listIterator(); //
while (lit.hasNext()){
String next = lit.next();
if (next.equals("a")){
lit.remove();
}
}
增加:看看集合中是否有 b元素, 我添加 一个c元素。( 只针对 List集合, 其他的集合 set Collection 你都用不了)
解决方案1: 集合自己去遍历,自己去添加
for (int i = 0; i < al.size(); i++) { // 这种只适用于List集合。
String s = al.get(i);
if(s.equals("b")){
al.add("c"); // 添加到了集合最后面
}
}
解决方案2: 迭代器 去遍历集合的时候, 让迭代器去添加元素。
ListIterator<String> lit = al.listIterator();
while (lit.hasNext()){
String next = lit.next();
if (next.equals("b")){
lit.add("c"); // 添加的是 光标坐在的位置。
}
}
列表迭代器 (☆☆☆):
1: ListIterator listIterator(); 是 List 体系 特有的方法。
2: Iterator 是 ListIterator 这个接口的 父接口
细节:
1: 因为我们如果使用 无参数的 ListIterator listIterator(); 拿到的这个列表迭代器的光标是在最前面的。
所以必须向后面编译一遍,把光标放到了最后,才能再向前遍历。
这种方式比较麻烦。
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
//System.out.println(it.next());
}
while (it.hasNext()){ // 此处不会进入循环。 因为 上一次的while循环已经把迭代器的指针放到了之后,所以此处判断,已经没有下一个元素了。
System.out.println(it.next());
}
}
}
public class Demo1 {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("aa");
al.add("aa1");
al.add("aa2");
al.add("aa3");
ListIterator<String> lit = al.listIterator(); // 你获取的迭代器 ,最开的时候 光标在最前面
while (lit.hasPrevious()){ //有前一个元素吗?
System.out.println(lit.previous());
} //并不会打印任何东西。
}
}
public class Demo1 {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("aa");
al.add("aa1");
al.add("aa2");
al.add("aa3");
ListIterator<String> lit = al.listIterator(); // 你获取的迭代器 ,最开的时候 光标在最前面
while (lit.hasNext()){
System.out.println(lit.next());
}
while (lit.hasPrevious()){
System.out.println(lit.previous());
}
/*
因为 获取迭代器的时候 ,获取的迭代器光标是在最前面,所以 不能直接 向前遍历。
只能先先后遍历一遍,然后 再向前遍历才可以。
这样做很麻烦。
我们就想 如果我们获取的迭代器的时候, 光标就再最后的话,就可以直接向前遍历了啊。
所有就有了下面的改进。
*/
ListIterator<String> lit = al.listIterator(al.size()); // 获取迭代器的时候 我就想让迭代器的光标放在最后。
while (lit.hasPrevious()){
System.out.println(lit.previous());
}
}
}
2: ListIterator listIterator(int index); 通过这个方法 就可以拿到光标在指定位置的 列表迭代器。
通过这种方式,拿到光标在最后的 列表迭代器, 就可以直接向前遍历了。
增强for循环(☆☆☆☆☆)
jdk1.5的时候 出现的新特性。 他就是为了简化迭代器遍历集合的代码的。 底层依然是用的迭代器。
只要实现了 Iterable 接口的所有的子类对象 都可以被增强for循环操作。
Iter 迭代
able 可以...的
Throw 扔出
able 可以...的 Throwable
格式:
for (元素的类型 变量 : 集合或者数组 ){ // 冒号后面的东西 必须是 实现了 Iterable接口 ,底层仍是迭代器
// 变量 就是代表的每个元素
}
证明:
1:冒号后面的东西 必须是 实现了 Iterable接口
2:增强for循环的底层是用的 迭代器
class Student implements Iterable<Integer>{
@Override
public Iterator<Integer> iterator() {
return new Itr();
}
private class Itr implements Iterator<Integer>{
@Override
public boolean hasNext() {
return true;
}
@Override
public Integer next() {
return 1;
}
}
}
Student s = new Student();
for (Object a:s){
System.out.println(a);
}
集合的遍历方式总结:
Collection : 2种 普通迭代器 增强for循环
List : 4种 普通迭代器 列表迭代器 普通for循环 增强for循环。
Set : 2种 普通迭代器 增强for循环
数据结构:
任何的容器 : 不都为了存储数据。
数据在容器当中的排列顺序,存储顺序,和结构。 这些就是数据结构。
栈: 先进后出 类比容器: 弹夹。
队列: 先进先出 类比现实生活中的容器: 水管。
数组: 查询快 增删慢
链接: 查询慢 增删快。
单向链表
双向链表: LinkedList
List 的子类:
ArrayList :数组: 查询快 增删慢
LinkedList :链接: 查询慢 增删快。(双向链表)
因为他的底层是双向链表,所以他里面有很多的和头尾相关的方法。
addFirst(E e);
addLast(E e);
removeLast();
removeFirst();
getFirst();
getLast();
链表结构,不像数组那样, 数组可以通过索引获取元素, 链表没有索引,所以链表要想查找元素,只能从头往后 或者从后往前查找。
List 里面 有一个特有方法 get(int index);
既然List接口有这个方法。
那么 LinkedList 也是有这个方法的。 那与 链表不能通过索引获取元素 岂不是矛盾吗?
没有矛盾的: LinkedList底层 调用此方法的时候,会先判断 你找的这个索引数 比 数组长度的一半 大还小。 如果比一半小
我就从前往后查找。 如果说比一半大,我就从后往前查找。
Set :不可以存储重复元素 (存取元素无序,没有索引,不能通过普通for循环遍历)
hashCode():
哈希值: 在java中 你看不到真实的物理地址。 jdk的Object中HashCode方法, 会根据对象的真实物理地址,结合哈希算法,给你算出唯一的一个值。
从而让这个值来代替 地址值。
Object的HashCode方法, 不同的对象的哈希值 是肯定不一样的。 相同对象的哈希值 是一样的。
但是我们可以通过重写HashCode方法,使得 不同对象的哈希值 相同。
比如:
String类 重写了Object的HashCode方法
int a = "重地".hashCode();
int b = "通话".hashCode();
System.out.println(a==b);//true
System.out.println("重地"=="通话"); // == 比的是真是的物理地址。 false
HashSet :
底层 是哈希表结构:
他什么保证元素唯一的呢?
HashSet 存储元素的时候: // //Object的HashCode方法, 不同的对象的哈希值 是肯定不一样的。 相同对象的哈希值 是一样的。hash表依赖于hashcode()和equals()方法
首先拿着元素的 HashCode值,和哈希表所有的哈希值去比。看看哈希表中,有没有存在存在和元素的哈希值相同的元素;
如果不存在 : 就直接把这个元素 添加到哈希表中
如果存在: 就让这个元素 拿着自己的equals方法,和哈希值相同的那些元素,依次去equals
如果比了一遍:都是false 则添加到集合中
如果比了一遍:有返回true的 则不添加集合(替换)
如果咱们让hashSet存储 Student对象
如果我认为 学生里面 姓名 年龄 相同了 我就认为是同一个人。 则Student 需要重写HashCode和equals方法
//默认情况下,不同对象的哈希值是不相同的
//通过方法重写,可以实现不同对象的哈希值是相同的
//System.out.println("重地".hashCode()); //1179395
// System.out.println("通话".hashCode()); //1179395 String重写了HashCode方法
重写 equals和HashCode方法 package com.itcast;
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name, int age){
this.name = name;
this.age = age;
}
/*@Override
public boolean equals(Object obj) {
Student s = (Student)obj;
return s.name.equals(name)&&s.age ==age;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true; ////直接比较??
if (o == null || getClass() != o.getClass()) return false; ////getClass方法???
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
LinkedHashSet :不可以存储重复元素 存取有序
TreeSet :不可以存储重复元素 可以给元素排序(从小到大 或者从大到小)
讨论题:
1:TreeSet 为什么能排序 底层是什么结构 ?
2:TreeSet 存 学生对象呢? 怎么排? 根据什么排序??
一: Student 是实现一个Comparable 接口 并重写 compareTo方法。并把排序的规则 写在 compareTo方法里面。
存学生的时候,就会按照 你写的规则去排序。
(引入一个 第三方裁判。裁判就可以给你的元素比出大小来 从而让你TreeSet知道谁大谁小,进而排序
这个裁判是谁呢? 是Comparator接口的子类对象。 子类对象里面必须具备 compare(s1,s2)方法。)
二:
小明 和 小红 比较身高。 第一种比较方式: 小明 自己和 小红去比。
第二种比较方式: 小明找了老师过来,给他评比一下。
懵点:
1:为什么 0 就是重复。 1就是就是 正序, -1就是 倒序。
2:为什么 this -s 就是升序, s-this 就是倒序。
//1、将字符串元素按照字典顺序排序,选择TreeSet集合。
//2、String实现了Comparable接口,默认排序就是字典顺序。所以不需要在TreeSet集合的构造方法中传入参数。
//3、TreeSet集合属于set体系,遍历方式为迭代器或者foreach语句。
题目1:
在集合里面 存储 10个 1-100的随机数。然后遍历
(要求集合里面的这10个随机数 不能重复。)
题目:2:(和今天第11个视频内容一样)
有一个学生类, 有这些属性, 数学成绩 math , 英语成绩 English , 语文 Chinese
有5个学生, 按照学生的总成绩 从大到小排序。
如果总成绩相同, 请按照 数学排
如果总成绩同了 数学也同了,语文排。
语文也同了,安英语排。
英语也同了,按姓名排。
姓名也同了,就是同一个人 去除掉。
题目3:
在集合里面 存储10个 1-20的随机数。
然后不能有重复,然后 从大到小排序。 打印出来。
(你可以试着做做 从大到小排序)
TreeSet ts = new TreeSet();
ts.add(100);
ts.add(99);
ts.add(101);
// 99 100 101
interface Inter {
void show(String s , String s1);
}
class MyInter implements Inter {
public void show(String s , String s1){
}
}
Inter mi = new MyInter();
mi.show("a","b");
Inter i = new Inter(){
public void show(String s , String s1){
}
};
i.show("abbc","bdc");
Cat extends Animal
Cat implements Jumping;
Animal a = new Cat();
//Jumping j = a; // 编译报错。
Jumping j = (Jumping)a ;// 编译成功 运行成功
标签:String,迭代,al,列表,lit,并发,add,集合 来源: https://www.cnblogs.com/zhang-blog/p/14249657.html