0%

HasSet说明

HasSet的底层其实调用了HasMap

1
2
3
public HashSet() {
map = new HashMap<>();
}

Hashset可以存放null,只能存放一个

HashSet不保证元素是有序的,取决与hash后,在确定索引的结果

练习1

简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.*;

public class Test {
public static void main(String [] args){
HashSet hashSet=new HashSet();
System.out.println(hashSet.add(1));
System.out.println(hashSet.add(1));
System.out.println(hashSet.add(1));
System.out.println(hashSet.add(1));
System.out.println(hashSet.add(2));
System.out.println(hashSet.add(3));
System.out.println(hashSet.add(4));
System.out.println(hashSet);
hashSet.remove(1);
System.out.println(hashSet);
}
}

image-20220704181147390

进阶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.*;

public class Test {
public static void main(String [] args){
HashSet hashSet=new HashSet();
System.out.println(hashSet.add("jack"));
System.out.println(hashSet.add("jack"));
System.out.println(hashSet.add(new Dog("jack")));
System.out.println(hashSet.add(new Dog("jack")));
System.out.println(hashSet.add(new String("rose")));
System.out.println(hashSet.add(new String("rose")));
System.out.println(hashSet.add("jack"));

//new操作之后,地址不同
System.out.println(hashSet);
}
}
class Dog{
String name;

public Dog(String name) {
this.name = name;
}
}

image-20220704181243139

HashSet的底层机制

HashSet的底层机制是HashMap,HashMap的底层是(数组+链表+红黑树)

数组+链表:提高存储的效率

image-20220704181253005而红黑树则是数组的存储到达64时,并且链表存储到达8时,形成层次性更强的树

HasSet的扩容机制

  1. 添加一个元素的时候,会先得到hash值,根据此值转成索引值
  2. 找到存放数据的表,看这个索引值对应的位置是否已经存放元素
  3. 如果没有则直接加入,如果有则调用equals比较,如果相同,就放弃添加(这也就解释了为什么不能添加相同的元素的原因),如果不相同,则添加到最后
  4. 在java8中如果一条链表个数超过8,并且table的大小>=64,会变成红黑树
1
2
3
4
5
6
7
8
9
10
import java.util.*;

public class Test {
public static void main(String [] args){
HashSet hashSet=new HashSet();
hashSet.add("java");
hashSet.add("lili");
hashSet.add("java");
}
}

在debug的模式下尝试第一次add,首先进入add方法

image-20220704181309618调用put方法,value是一个占位符,用于满足hashmap的方法

image-20220704181321325而这里的算法得到的值是将key.hashcode右移16位

image-20220704181334435而我们第一刺进putVal方法时,此时的table[]为空,调用resize方法进行扩容为16位,同时该方法也会检测table数组的使用是否达到了12(16*加载因子(0.75))位,如果达到,就再一次进行扩容达到32,而检测是树也达到了24

image-20220704181410967

image-20220704181525397

而当我们进行第二次扩容,经过内置的算法计算出lili应该放在8号索引位置,步骤与上述的相同

image-20220704181438068但是当我们进行第三次扩容时,由于加入相同的字符串对象,算法算出的hash值相等,得出同样都在3的索引位置,然后进行equals的比较,比较他们的值是否相同。

总结

针对上面的练习的进阶,我们运行以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.*;

public class Test {
public static void main(String [] args){
String str1="jack";
String str2="jack";
String str3=new String("lili");
String str4=new String("lili");
Dog dog1=new Dog("as");
Dog dog2=new Dog("as");
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(str3.hashCode());
System.out.println(str4.hashCode());
System.out.println(dog1.hashCode());
System.out.println(dog2.hashCode());
}
}
class Dog{
String name;

public Dog(String name) {
this.name = name;
}
}

image-20220704181543608所以说hashset能否允许新元素的加入,key.hashCode()决定了它在数组的索引位置,而最终的比较还需要看equals方法的定义,当然在比较的过程中系统调用了equals方法,程序员也可以自己定义重写equals方法,改变比较的规则,比较hashcode是系统默认给出的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.*;

public class Test {

public static void main(String [] args){

String str1="jack";
String str2="jack";
String str3=new String("lili");
String str4=new String("lili");
Dog dog1=new Dog("as");
Dog dog2=new Dog("as");
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(str3.hashCode());
System.out.println(str4.hashCode());
System.out.println(dog1.hashCode());
System.out.println(dog2.hashCode());
HashSet hashSet=new HashSet();
hashSet.add(dog1);
hashSet.add(dog2);
hashSet.add(str1);
hashSet.add(str2);
hashSet.add(str3);
hashSet.add(str4);
System.out.println(hashSet);
}
}
class Dog{
String name;

public Dog(String name) {
this.name = name;
}

@Override
public int hashCode(){
return 100;
}

@Override
public boolean equals(Object o) {
if (this == o) {return true;}
if (o == null || getClass() != o.getClass()) {return false;}
Dog dog = (Dog) o;
return Objects.equals(name, dog.name);
}
}

image-20220704181952021练习1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.HashSet;
import java.util.Objects;

public class Test {
public static void main(String[] args) {
HashSet hashSet=new HashSet();
hashSet.add(new E("DSC","12"));
hashSet.add(new E("DSC","12"));
hashSet.add(new E("DSC","12"));
System.out.println(hashSet);
}
}
class E{
String name;
String age;

public E(String name, String age) {
this.name = name;
this.age = age;
}

/*@Override
public boolean equals(Object obj){
if (obj==null){
retur
}
}*/
//比较两个的值是否相同

@Override
public boolean equals(Object o) {
if (o==this){
return true;
}
E e=(E) o;
if (Objects.equals(age,e.age)&&Objects.equals(name,e.name)){
return true;
}
else{return false;}
}
//将所有员工放入到一个数组格格中
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

image-20220704182022266练习2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Objects;

public class Test {
public static void main(String[] args) {
LocalDate localDate1= LocalDate.parse("2021-02-03");
Employee employee1=new Employee("阿硕",12,localDate1);
LocalDate localDate2= LocalDate.parse("2021-02-03");
Employee employee2=new Employee("阿硕",12,localDate2);
HashSet hashSet=new HashSet();
hashSet.add(employee1);
hashSet.add(employee2);
System.out.println(hashSet.toString());
}
}
class Employee{
String name;
int age;
LocalDate birthday;

public Employee(String name, int age, LocalDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
@Override
public int hashCode(){
return Objects.hash(name,birthday);
}
@Override
public boolean equals(Object o){
if (o==this){
return true;
}
Employee employee=(Employee) o;
if (Objects.equals(this.birthday,employee.birthday)&&Objects.equals(this.name,employee.name)){
return true;
}
else {
return false;
}
}

@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}

image-20220704182104594练习3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.util.*;

public class Test {
public static void main(String[] args) {
Person p1=new Person("花花","12");
Person p2=new Person("猫猫","25");
HashSet hashSet=new HashSet();
hashSet.add(p1);
hashSet.add(p2);
System.out.println(p1.hashCode());
p1.name="ahua";
System.out.println(p1.hashCode());
System.out.println(hashSet);
System.out.println(hashSet.remove(p1));
System.out.println(hashSet);
}
}
class Person{
public String name;
public String id;

public Person(String name, String id) {
this.name = name;
this.id = id;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
return Objects.equals(name, person.name) && Objects.equals(id, person.id);
}

@Override
public int hashCode() {
return Objects.hash(name, id);
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
'}';
}
}

image-20220704182141502

Hahstable的说明

  1. hashtable的键和值都不能为空
  2. hashTable使用方法基本和HashMap一样
  3. hashTable是线程安全的,hashMap是线程不安全的
  4. 其底层也是一个数组,初始化为11,临界值为8
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class Test {
public static void main(String[] args) {
Hashtable hashtable=new Hashtable();
hashtable.put("法拉利","123456元");
hashtable.put(null,null);

}
}

image-20220312155153819Hashtable与hashMap的比较

HashMap 线程不安全 允许空值
Hashtable 安全 较低 不允许空值

Properties说明

Properties可以从properties文件中,加载到Properties对象,并且进行读取和修改,经常作为配置文件,继承了Hashtable,其对应的方法也相同。

LinkedList说明

LinkedList底层实现了一个双向链表,下面图中由first与last元素,分别标识双向链表的头和尾。而他们维护的双向链表中节点又拥有prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表。

所以LinkedLIst的元素的添加和删除不是通过数组,相对效率比较高,添加删除元素只需要控制节点的指向就ok。但是LInkedList是线程不安全的。

LinkedList的底层结构

1
2
3
4
5
6
7
8
9
10
import java.util.*;

public class Test {
public static void main(String [] args){
LinkedList linkedList=new LinkedList();
linkedList.add(1);

}
}

我们在debug情况下执行上述代码,发现进行add的时候,第一步,对传入的1进行装箱

紧接着进入到一个boolean的add方法,执行linklast方法,将这个数加入到这个双向链表的最后

image-20220308145644770

在linkLast方法中,在双向链表中加new一个新的节点,如果这个为null则把数存放在这里,就使next指向下一个节点中。由于此时链表中一个数也没有存放,所以first与last都指向节点1

image-20220308145900611

可以发现,first与next的指向的地址都相同

image-20220308150942411

此时如果我们再一次add一个数,并且用debug运行,再一次运行到linkLast时,新建立一个节点,last指向新节点,而此时l不为空,则l的next指向newNode

image-20220308151137821

练习

前面的操作与添加操作,而删除操作的流程

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.*;

public class Test {
public static void main(String [] args){
LinkedList linkedList=new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.remove(1);


}
}

我们在debug条件下运行删除操作,第一步进入到remove的源码

image-20220308152529515

而第一行的函数作用时判断想要删除的元素是否超过了范围,超出范围就抛出异常image-20220308152624085

而第二行的unlink函数则是真正进行双向链表操作的函数,简单一点说就是判断删除的位置在哪里,判断前面和后面是否还有节点,last,next,prev指向哪里???当然remove还有其他多种的删除方式,每一种方式不同

image-20220308152807338

image-20220308152905813

ArrayList与LinkList的比较

底层结构 增删的效率 改查的效率
ArrayList 可变的数组 较低,通过数组进行扩容 较高
LinkedList 双向的链表 较高,通过链表进行追加 较低,因为更改需要改变前后的节点的指向

在一个项目中,大部分都是查询,因此大部分会选择ArrayList。同时两个类都是线程不安全的

LinkedHashSet的简介

  1. LinkedHashSet是HashSet的子类
  2. 底层是一个LinkedHashMap,底层维护的一个数组+双向链表
  3. 根据元素hascode的值决定元素存储的位置,同时使用链表维护元素 的次序图,这使得元素看起来是以插入顺序保存的。双向链表的pre和next指向前一个和下一个元素,看起来有序
  4. LinkedHashSet不允许添加重复的元素

而LinkedHashSet的添加机制与HashSet相同

第一次添加时,直接将数组table【】扩容到16,存放的节点类型时LinkedHashSet$Entry,而如果想要将其放入到其中,则Entry应该使继承了Node的

image-20220312101443718

image-20220704182416122

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.time.LocalDate;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;

public class Test {
public static void main(String[] args) {
LinkedHashSet linkedHashSet=new LinkedHashSet();
linkedHashSet.add(new Car("奥迪",46465));
linkedHashSet.add(new Car("奥迪",46465));
linkedHashSet.add(new Car("奥迪",46465));
System.out.println(linkedHashSet);
}
}
class Car{
String name;
double value;

public Car(String name, double value) {
this.name = name;
this.value = value;
}

@Override
public int hashCode(){
return Objects.hash(name,value);
}

@Override
public boolean equals(Object o){
if (o==this){
return true;
}
Car car=(Car) o;
if (Objects.equals(car.name,name)&&car.value==value){
return true;
}
else {return false;}
}
}

image-20220704182424042

Map接口的特点

  1. Map保存具有映射关系的数据key:value(双列元素)
  2. 其结果还是无序的,其中key与value可以是任意数据的类型
  3. 同时其结果还是无序的,当有相同的key时,就相当于替换
  4. Map与value都可以为空,但是key只能是一个为空,value可以有多个空
  5. 常用String作为key,key常用一对一的关系,一个key对应一个value
  6. Map存放数据的key-value,一个k-v是放在一个Node中,因为Node实现了Entry接口,也可说一对k–v就是一个Entry
  7. 线程不安全的,没有做方法上的同步互斥操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.time.LocalDate;
import java.util.*;

public class Test {
public static void main(String[] args) {
Map map=new HashMap();
map.put("姓名","zhang");
map.put("姓名","zhang");
map.put("年龄",96);
System.out.println(map);
map.put("姓名","王");
map.put(null,"啊");
map.put(null,"moqi");
System.out.println(map);
}
}

image-20220312104437597

k-v后是HashMap$Node,k-v为了方便程序员遍历,还需创建EntrySet集合,该集合存放的数据类型Entry,而一个Entry对象就有k,v,EntrySet<Entry<k,v>>,entrySet中,定义的类型是Map.entry,但是实际上存放的是HashMap$Node,因为他继承了Map.Entry,而继承方便是为了方便遍历,是因为entry提供了getValue与getKey的方法

1
2
3
4
5
6
7
8
9
10
11
import java.time.LocalDate;
import java.util.*;

public class Test {
public static void main(String[] args) {
HashMap hashMap=new HashMap();
hashMap.put("sdv","sav");
hashMap.put("huawei","sd");
hashMap.put(new String("阿会"),new String("鬼"));
}
}

我们debug以上的代码,将table的数组转换成entry中,entry中的值指向了table地址,而同样也有keyset方法可以直接去除其键,而不去除其值。

image-20220312111911202

Map接口的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.time.LocalDate;
import java.util.*;

public class Test {
public static void main(String[] args) {
Map map=new HashMap();
map.put("奥迪","1256");
//发生替换
map.put("奥迪","4523542543");
map.put("奔驰","45235425");
System.out.println(map);
map.remove("奔驰");
System.out.println(map);
System.out.println(map.get("奥迪"));
System.out.println(map.size());
//查找键是否存在
System.out.println(map.containsKey("奔驰"));
//判断是否为空
System.out.println(map.isEmpty());
//清除键值对的关系
map.clear();
System.out.println(map);
}
}

image-20220312113018419

Map的遍历方法

containsKey:查找键是否存在

keySet:获取所有的键

entrySet:获取所有的关系

values:获取所有发值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.time.LocalDate;
import java.util.*;

public class Test {
public static void main(String[] args) {
Map map=new HashMap();
map.put("奥迪","1256");
//发生替换
map.put("奔驰","45235425");
map.put("打本","1000");
//遍历方式1
Set keyset=map.keySet();
for (Object key:keyset){
System.out.println(key+"-"+map.get(key));
}
//遍历方式2
Iterator iterator= keyset.iterator();
while (iterator.hasNext()){
Object key=iterator.next();
System.out.println(key+"-"+map.get(key));
}

//遍历方式3
Collection values=map.values();
for (Object key:values){
System.out.println(key);
}


//遍历方式3通过entry来获取k-v
Set entrySet=map.entrySet();
for (Object key:entrySet){
Map.Entry m=(Map.Entry) key;
System.out.println(m.getKey()+"-"+m.getValue());
}
//迭代器求法
Iterator iterator1=entrySet.iterator();
while (iterator1.hasNext()){
Object next=iterator1.next();
//此时
System.out.println(next);
}


}
}

image-20220312134547645

HashMap的底层机制

数组(table)+链表(每一个节点实现了HashMap$Node,同时node接口实现了Map$Entry)

jdk8之后底层实现了数组+链表+红黑树

其底层机制与HashSet相同,因为hashSet底层实现了hashmap,同时树化的条件也相同

Set接口的基本介绍

  1. 无序的,添加和取出的顺序不一致,没有索引,但是一旦我们取出之后,这个顺序就不会发生改变了,底层有一个算法,会对其进行顺序处理。
  2. 不允许重复的元素,所以最多只能含有一个null
  3. 和List接口一样,set接口也是Collection的子接口,因此,常用方法和Collection接口一样
  4. Set接口的遍历方式:使用迭代器或者增强for循环,不能使用索引的方式来获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.*;

public class Test {
public static void main(String [] args){
Set set=new HashSet();
set.add(1);
set.add(2);
set.add(3);
set.add(1);
set.add(1);
set.add(1);
set.add(null);
set.add(null);
set.add(null);
//可以发现我们添加了许多重复的元素,但是只输出了一个,而且null是最后添加的,但是却第一个输出
System.out.println(set);
Iterator iterator= set.iterator();
while (iterator.hasNext()){
Object obiect =iterator.next();
System.out.println(obiect);
}

}
}

image-20220308161055228

TreeSet说明

treeSet可以进行排序,我们如果不进行处理,则排序是乱的

  • 排序

image-20220312161951888

可以看到Tree拥有很多的构造器

image-20220312162057921

使用TreeSet提供的一个构造器,并且传入一个匿名内部类,并指定排序规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.*;

public class Test {
public static void main(String[] args) {
TreeSet treeSet=new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ( (String)o2).compareTo((String)o1);
}
});
treeSet.add("as");
treeSet.add("ds");
treeSet.add("cs");
treeSet.add("es");
treeSet.add("jack");
treeSet.add("array");
System.out.println(treeSet);

}
}

image-20220312162903875

我们debug运行一下,可以看到构造器中其底层其实调用了treemap的构造器方法

image-20220312163645971

而接下来,我们运行的treeset的put方法

image-20220312163849513

动态绑定到我们的cpmpare啊方法中执行我们的方法

image-20220312163916727

TreeMap说明

其比较的方法与上述相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.*;

public class Test {
public static void main(String[] args) {
TreeMap treeMap=new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ( (String)o2).compareTo((String)o1);
}
});
treeMap.put("as","haha");
treeMap.put("bs","haha");
treeMap.put("ij","haha");
treeMap.put("arr","haha");
treeMap.put("bt","haha");
System.out.println(treeMap);

}
}

image-20220312164405976

下面我们进行根据字符串的大小进行比较,因为这个TreeMap返回的compare当长度相等为0,进行舍弃,顾可以发现bt并没有打出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.*;

public class Test {
public static void main(String[] args) {
TreeMap treeMap=new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ( ((String)o2).length())-(((String)o1).length());
}
});
treeMap.put("as","haha");
treeMap.put("bsfgn","haha");
treeMap.put("ijgf","haha");
treeMap.put("arr","haha");
treeMap.put("bt","haha");
System.out.println(treeMap);

}
}

image-20220312165111731

Vector介绍

List接口的实现子类,

1
2
3
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

Vector的底层也是对象数组,protected Object[] elementData;

同时Vector类是线程同步的,是线程安全的。

Vector源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;

public class Test {
public static void main(String [] args){
Vector vector=new Vector();
for (int i=0;i<10;i++){
vector.add(vector);
}

vector.add(10);


}
}

在debug模式下,默认初始赋值为10

image-20220308093438603

而当我们超过10的时候,底层进行扩容,数量变成原来的两倍,其机制与ArrayList大同小异。

image-20220308093549059

image-20220308094449176

List接口基本介绍

首先明确,List接口是Collection接口的子接口

  • List集合类中元素有序,与其加入的顺序一致,并且里面的内容可以重复。
  • List集合中每一个元素都有器对应的顺序索引,即支持索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class Test {
public static void main(String [] args){

Dog dog1=new Dog("ahhua",12);
Dog dog2=new Dog("a",15);
Dog dog3=new Dog("ah",13);
Dog dog4=new Dog("ah",13);
//迭代器遍历
List list=new ArrayList();
list.add(dog1);
list.add(dog2);
list.add(dog3);
list.add(dog4);
//支持索引
System.out.println(list.get(2));
for (Object a:list){
System.out.println(a);
}

}
}
class Dog{
private String name;
private int age;

public Dog(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

image-20220704180534499List接口常用的方法

除了我们前面用到的方法

  • 插入一个对象, add方法
  • 返回元素所在的第一个位置/最后的位置 indexOf/lastIndexOf
  • 移除指定index位置的元素,并且返回此元素 remove
  • 设定指定位置为某个元素 set
  • 返回某个范围的子集 sublist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class Test {
public static void main(String [] args){

Dog dog1=new Dog("ahhua",12);
Dog dog2=new Dog("a",15);
Dog dog3=new Dog("ah",13);
Dog dog4=new Dog("ah",13);
//迭代器遍历
List list=new ArrayList();
list.add(dog2);
list.add(dog1);
list.add(dog2);
list.add(dog3);
//dog4插入到前面
list.add(0,dog4);
//返回位置
System.out.println(list.indexOf(dog2));
System.out.println(list.lastIndexOf(dog2));
//移除指定的元素,并且返回
System.out.println(list.remove(2).toString());
//指定位置为指定元素
list.set(1,dog4);
System.out.println(list.subList(0,2));

}
}
class Dog{
private String name;
private int age;

public Dog(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

image-20220704180643800List遍历方式

与Collection遍历相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class Test {
public static void main(String [] args){

Dog dog1=new Dog("ahhua",12);
Dog dog2=new Dog("a",15);
Dog dog3=new Dog("ah",13);
Dog dog4=new Dog("ah",13);

List list=new ArrayList();
list.add(dog2);
list.add(dog1);
list.add(dog2);
list.add(dog3);
//迭代器
Iterator iterator=list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());

}
for (Object o :list) {
System.out.println(o);
}


}
}
class Dog{
private String name;
private int age;

public Dog(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

image-20220704180742962

迭代器

  1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
  2. 所有实现了Collection接口的集合类都有一个Iterator()方法,用以返回一个实现了iterator接口的对下个,即可以返回一个迭代器。
  3. Iterator的结构
  4. Iterator仅用于遍历集合,Iterator本身并不存放对象。

迭代器执行原理

image-20220704175415899

1
2
3
4
5
6
7
8
//对于remove方法,还方法删除由next返回的最新项,此后我们不能调用remove,直到next再一次调用之后
public interface Inerator<E>
{
boolean hasNext();
E next();
void remove();
}

迭代器的删除操作与Collection的remove方法其实效率快很多,当我们一个集合中拥有213456,我们要删除其中的偶数,结果应该为135,如果调用collection方法,每一次删除之前都要重新遍历,事件开销比较大,但是如果使用迭代器的remove方法,效率会快很多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package 其他;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
* @author 21050
*/
public class IteratorTest {
public static void main(String[] args) {
LinkedList<Integer> list=new LinkedList<>();
list.add(2);
list.add(1);
list.add(3);
list.add(5);
IteratorTest iteratorTest=new IteratorTest();
iteratorTest.removeEvenVer(list);
}

public void removeEvenVer(List<Integer> list){
Iterator<Integer> integerIterator= list.iterator();
while (integerIterator.hasNext()){
if (integerIterator.next()%2==0){
integerIterator.remove();
}

}
}
}

这时有的小伙伴会说我们使用增强for循环,不一样可以实现这个吗

1
2
3
4
5
6
7
public void remove(List<Integer> list){
for (Integer i:list){
if (i%2==0){
list.remove(i);
}
}
}

image-20220614102634061

但是我们要注意,增强for循环所带来的弊端,增强for循环底层的实现基础迭代器,但是在这个过程中modcount与expectedModCount不同步导致的,在最后的判断中抛出异常,在进行删除添加等操作的时候,尽量不要使用增强for循环。

Java ConcurrentModificationException异常原因和解决方法 - Matrix海子 - 博客园 (cnblogs.com)

Iterator iterator=coll.iterator();//得到一个集合的迭代器

//hasNext()判断是否还有下一个元素

//next()指针向下启动,将下移以后集合位置上的元素返回,所以调用next方法之前,则应该先调用hasNext方法来判断是否已经结束,否则可能会发生异常。

image-20220307195612166

我们运行以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Test {
public static void main(String [] args){
Collection col= new ArrayList();
col.add(123);
col.add(56);
col.add("jack");
col.add(10);

//要想遍历col集合,需要先得到col对应的迭代器Iterator
Iterator iterator=col.iterator();
//利用while循环进行遍历
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("遍历完毕");
//重置迭代器
iterator= col.iterator();
//下面可以开始二次遍历

}
}
image-20220704175559182

注意:当我们while循环结束后,这时的迭代器已经指向最后的元素,如果我们再一次输出,就会发生报错。若我们想要在同一次运行中再一次遍历这个集合,则需要重置我们的迭代器

增强for循环遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;
import java.util.Collection;

public class Test {
public static void main(String [] args){
Collection col= new ArrayList();
col.add(123);
col.add(56);
col.add("jack");
col.add(10);

for (Object a:col){
System.out.println(a);
}

}
}

image-20220704175649647

注意,虽然还是for循环,但是本质还是利用了迭代器,利用debug的方法可以进入,是简化版本的die’dai’qi

image-20220704175711885练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Test {
public static void main(String [] args){

Dog dog1=new Dog("ahhua",12);
Dog dog2=new Dog("a",15);
Dog dog3=new Dog("ah",13);
//迭代器遍历
Collection collection=new ArrayList();
collection.add(dog1);
collection.add(dog2);
collection.add(dog3);
Iterator iterator=collection.iterator();

while (iterator.hasNext()){
System.out.println(iterator.next());
}
//迭代器指针归位
iterator=collection.iterator();
for (Object a:collection){
System.out.println(a);
}

}
}
class Dog{
private String name;
private int age;

public Dog(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

image-20220704175724420