目录
3、并发修改异常(ConcurrentModificationException)
一、集合概述
(1)集合的特点:提供一种存储空间可变的模型,存储的数据容量可以随时发生改变。
(2)集合与数组的区别
1.长度上的区别:数组的长度是固定,设定后就无法变化的,而集合的存储长度是动态可变,相较于数组来说更加的灵活,能满足更多的需求。
2.存储内容上的区别:对于数组来说,数组的存储内容可以是基本类型,也可以是引用类型,然而集合的存储内容只能是引用类型。
3.存储元素上的区别:对于数组来说,数组的存储元素有且仅有一种类型,然而集合可以存储不同类型的存储元素,即集合存储元素类型不止一种。
(3)集合的体系结构
集合分为单列集合Collection及双列集合Map,其中单列集合Collection下又分可重复集合List与不可重复集合Set等。此处提及的Collection、Map、List、Set集合全为接口,其不可实例化需要对应的实现类来实现。
集合体系结构框架图:
二、Collection
1、Collection集合概述
(1)是单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
(2)JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
2、创建Collection集合的对象
(1)以多态的方式来创建对象
(2)具体的实现类ArrayList
如接口Collection以多态的方式,通过实现类ArrayList来实现接口的中的方法add();
下面是代码示例:
/**
* @author Tweek
*/
public class CollectionDemo1 {
/**
* 创建Collection集合对象的方法
* 以多态的方式 ArrayList()
*/
public static void main(String[] args) {
//创建Collection集合的对象
Collection<String> collection = new ArrayList<String>();
//添加对象
collection.add("Have");
collection.add("A");
collection.add("Nice");
collection.add("Day ");
//输出集合对象
System.out.println(collection);
}
}
控制台输出:
[Have, A, Nice, Day ]
3、Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
下面是代码示例,能让我们更好地了解Collection的常用方法:
/**
* @author Tweek
*/
public class CollectionDemo2 {
/**
* 1.bool add(E e)
* 2.bool remove(Object o)
* 3.int size()
* 4.bool contains(Object o)
* 5.bool isEmpty()
* 6.void clear()
*/
public static void main(String[] args) {
//创建集合对象
Collection<String> cl = new ArrayList<String>();
//1.添加元素add()
cl.add("Tweek");
cl.add("Meg");
cl.add("Chris");
cl.add("Peter");
cl.add("Louis");
System.out.println("Use the method 'add'");
System.out.println(cl);
System.out.println("---------------------------------");
//2.移除指定元素
cl.remove("Meg");
System.out.println("Use the method 'remove'");
System.out.println(cl);
System.out.println("---------------------------------");
//3.集合长度
System.out.println(cl);
System.out.println("The size of This Collection: "+cl.size());
System.out.println("---------------------------------");
//4.判断集合中是否存在指定元素
System.out.println(cl);
System.out.println("'Peter' is in This Collection! True or False?");
System.out.println(cl.contains("Peter"));
System.out.println("---------------------------------");
//5.判断集合是否为空
System.out.println(cl);
System.out.println("This Collection is Empty! True or False? ");
System.out.println(cl.isEmpty());
System.out.println("---------------------------------");
//6.清空集合元素,并判断是否为空
System.out.print("Before 'clear' :");
System.out.println(cl);
cl.clear();
System.out.print("After 'clear' :");
System.out.println(cl);
System.out.println("This Collection is Empty! True or False? ");
System.out.println(cl.isEmpty());
}
}
控制台输出:
Use the method 'add'
[Tweek, Meg, Chris, Peter, Louis]
---------------------------------
Use the method 'remove'
[Tweek, Chris, Peter, Louis]
---------------------------------
[Tweek, Chris, Peter, Louis]
The size of This Collection: 4
---------------------------------
[Tweek, Chris, Peter, Louis]
'Peter' is in This Collection! True or False?
true
---------------------------------
[Tweek, Chris, Peter, Louis]
This Collection is Empty! True or False?
false
---------------------------------
Before 'clear' :[Tweek, Chris, Peter, Louis]
After 'clear' :[]
This Collection is Empty! True or False?
true
4、Collection集合的遍历
(1)terator
Iterator:迭代器,集合的专用遍历方式。其本质是一个接口,故不能实例化,需要实现类来实现,即需以多态的方式。
1.Iterator<E>iterator(): 返回此集合中元素中的迭代器,通过集合的iterator()方法得到
2.迭代器是依赖于集合而存在的
3.格式
//创建集合
Collection<String> cl = new ArrayList<String>();
//遍历集合,迭代器方法
//Iterator<E> 迭代器名 = 集合名.iterator()
Iterator<String> it = cl.iterator();
(2)Iterator的常用方法
方法名 | 作用 |
---|---|
E next() | 返回迭代中的下一个元素 |
boolean hasNext() | 如果迭代具有更多元素,则返回true |
1.E next()
利用此方法获取集合中的元素
下面是代码示例:
/**
* @author Tweek
*/
public class CollectionDemo3 {
public static void main(String[] args) {
//创建集合对象
Collection<String> cl = new ArrayList<String>();
//添加元素
cl.add("Tweek");
cl.add("Meg");
cl.add("Chris");
cl.add("Peter");
cl.add("Louis");
// Iterator<E> iterator()。返回此集合中元素的迭代器,通过集合的iterator方法()得到
Iterator<String> it = cl.iterator();
// E next()
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
}
}
控制台输出:
Tweek
Meg
Chris
Peter
Louis
2.boolean hasNext()
判断能否继续迭代,即判断是否存在下一个元素。
下面是代码示例:
/**
* @author Tweek
*/
public class CollectionDemo3 {
public static void main(String[] args) {
//创建集合对象
Collection<String> cl = new ArrayList<String>();
//添加元素
cl.add("Tweek");
cl.add("Meg");
cl.add("Chris");
cl.add("Peter");
cl.add("Louis");
// Iterator<E> iterator()。返回此集合中元素的迭代器,通过集合的iterator方法()得到
Iterator<String> it = cl.iterator();
//boolean hasNext()
while(it.hasNext()){
System.out.println(it.next());
}
//迭代完集合内元素后再判断是否存在下一个元素
//此处hasNext判断为false,故无输出
if(it.hasNext()){
System.out.println(it.next());
}
}
}
控制台输出:
Tweek
Meg
Chris
Peter
Louis
(3)综合例子
例子:创建学生类,Collection存储学生类对象,并遍历
下面的代码示例,能让我们更好地理解迭代器:
/**
* @author Tweek
*/
public class Student {
/**
学生类
*/
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
public class CollectionStudentsTest {
/**
* Collection存储学生类对象,并遍历
* @param args
*/
public static void main(String[] args) {
//创建学生类对象
Student stu1 = new Student("Tweek",16,100);
Student stu2 = new Student("Peter",32,54);
Student stu3 = new Student("Chris",17,65);
Student stu4 = new Student("Meg",17,0);
Student stu5 = new Student("Louis",32,87);
//创建集合
Collection<Student> stucl = new ArrayList<Student>();
stucl.add(stu1);
stucl.add(stu2);
stucl.add(stu3);
stucl.add(stu4);
stucl.add(stu5);
//遍历集合,迭代器方法
Iterator<Student> it = stucl.iterator();
while(it.hasNext()){
Student s = it.next();
System.out.println("Name: "+s.getName()+" Age: " +
s.getAge()+
" Score: "+s.getScore());
}
}
}
三、List集合
1、List集合概述和特点
(1)概述
List集合是有序集合(也称序列),使用List集合,用户可以控制列表中每一个元素的插入位置,用户也可以通过整数索引来访问元素,也可以用整数索引来搜索列表中的元素。
与Set集合不同的是,List集合允许重复的元素,而Set集合不允许有重复的元素
(2)特点
1.有序:存储和取出的元素顺序一致,类似于队列,FIFO先进先出
2、可重复:存储的元素可以重复
下面的代码示例,能让我们更好地理解List的特点
/**
* @author Tweek
*/
public class ListDemo1 {
/**
List的特点
1、有序
2、重复
*/
public static void main(String[] args) {
//创建List集合
List<String> ls = new ArrayList<>();
//添加元素,集合中出现重复元素Tweek
ls.add("Tweek");
ls.add("Meg");
ls.add("Chris");
ls.add("Peter");
ls.add("Louis");
ls.add("Tweek");
//用迭代器遍历
Iterator<String> it = ls.iterator();
//若有序,则list中的元素顺序与添加元素顺序一致
while(it.hasNext()){
System.out.println(it.next());
}
}
}
控制台输出:
Tweek
Meg
Chris
Peter
Louis
Tweek
2、List集合的特有方法
方法名 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定元素 |
E remove(int index) | 删除指定索引位置的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引位置的元素,返回被修改的元素 |
E get(int index) | 返回指定索引位置的元素 |
下面的代码示例,能让我们更好地理解List集合的特有方法:
/**
* @author Tweek
*/
public class ListDemo2 {
/**
* List集合的特有方法
* 1.void add(int index,E element)
* 2.E remove(int index)
* 3.E set(int index,E element)
* 4.E get(int index)
*/
public static void main(String[] args) {
//创建集合
List<String> ls = new ArrayList<>();
//1.添加元素add()
addElement(ls);
//2.移除索引位置元素并返回移除元素
removeElement(ls);
//3.修改索引位置元素并返回被修改元素
changeElement(ls);
//4.返回索引位置元素
getElement(ls);
}
private static void addElement(List<String> ls) {
ls.add("Tweek");
ls.add("Meg");
ls.add("Chris");
ls.add("Peter");
ls.add("Louis");
ls.add("Tweek");
System.out.print("我添加了这些元素: ");
System.out.println(ls);
System.out.println("---------------------------");
}
private static void getElement(List<String> ls) {
int index = 0;
System.out.print("我获取到第"+(index+1)+"个元素:");
System.out.println(ls.get(index));
}
private static void changeElement(List<String> ls) {
int index=1;
System.out.print("修改第"+index+"元素前: ");
System.out.println(ls);
System.out.print("被修改的元素为:");
System.out.println(ls.set(index,"Brian"));
System.out.print("修改第"+index+"元素后: ");
System.out.println(ls);
System.out.println("---------------------------");
}
private static void removeElement(List<String> ls) {
int index=5;
System.out.print("移除第"+index+"元素前: ");
System.out.println(ls);
System.out.print("所移除的元素为:");
System.out.println(ls.remove(index));
System.out.print("移除第"+index+"元素后: ");
System.out.println(ls);
System.out.println("---------------------------");
}
}
控制台输出:
我添加了这些元素: [Tweek, Meg, Chris, Peter, Louis, Tweek]
---------------------------
移除第5元素前: [Tweek, Meg, Chris, Peter, Louis, Tweek]
所移除的元素为:Tweek
移除第5元素后: [Tweek, Meg, Chris, Peter, Louis]
---------------------------
修改第1元素前: [Tweek, Meg, Chris, Peter, Louis]
被修改的元素为:Meg
修改第1元素后: [Tweek, Brian, Chris, Peter, Louis]
---------------------------
我获取到第1个元素:Tweek
3、综合例子
创建一个存储学生对象的集合,存储3个学生对象,并在遍历学生对象信息打印在控制台
下面的例子代码,能让我们更好地理解List集合
/**
* @author Tweek
*/
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
public class ListStudentsTest {
public static void main(String[] args) {
//创建学生类对象
Student stu1 = new Student("Tweek",16,100);
Student stu2 = new Student("Peter",32,54);
Student stu3 = new Student("Chris",17,65);
Student stu4 = new Student("Meg",17,0);
Student stu5 = new Student("Louis",32,87);
//创建List集合
List<Student> ls = new ArrayList<>();
ls.add(stu1);
ls.add(stu2);
ls.add(stu3);
ls.add(stu4);
ls.add(stu5);
//遍历集合,用迭代器的方法
System.out.println("遍历List集合,用迭代器");
Iterator<Student> it = ls.iterator();
while(it.hasNext()){
Student stu = it.next();
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("------------------------------------------");
//for循环方法
System.out.println("遍历List集合,用for循环");
for (int i = 0; i < ls.size(); i++) {
Student stu = ls.get(i);
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
控制台输出:
遍历List集合,用迭代器
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
------------------------------------------
遍历List集合,用for循环
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
3、并发修改异常(ConcurrentModificationException)
(1)并发修改异常
当不允许这样修改时,可以通过检测到对象的并发修改方法来抛出此异常。像是说,一个线程通常不允许修改集合,而另一个线程正在遍历这个集合,就会出现并发修改异常。
下面的代码示例给出的例子,能让我们更好的了解并发修改异常的发生情景:
/**
* @author Tweek
*/
public class ListDemo1 {
/**
List集合正在遍历的同时进行元素添加操作,会出现并发修改异常
*/
public static void main(String[] args) {
//创建List集合
List<String> ls = new ArrayList<>();
//添加元素
ls.add("Tweek");
ls.add("Meg");
ls.add("Chris");
ls.add("Peter");
ls.add("Louis");
//用迭代器遍历
Iterator<String> it = ls.iterator();
//遍历集合的同时添加元素
while(it.hasNext()){
String s = it.next();
if(s.equals("Meg")){
ls.add("Meg");
}
}
}
}
/**
此时控制台提示出现ConcurrentModificationException(并发修改异常)与提示next()方法中的checkForComodification方法出了问题
*/
控制台输出:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at Collection.itemout.mList.ListDemo1.main(ListDemo1.java:29)
下面的代码示例是解决集合遍历中添加与if判断中调用equals函数的字符串常量不同的元素不出现并发修改异常的方法:
/**
* @author Tweek
*/
public class ListDemo3 {
/**
遍历集合的过程中,添加元素
*/
public static void main(String[] args) {
//创建List集合
List<String> ls = new ArrayList<>();
//添加元素
ls.add("Tweek");
ls.add("Meg");
ls.add("Chris");
ls.add("Peter");
ls.add("Louis");
//遍历集合的同时,添加元素
for (int i = 0; i < ls.size(); i++) {
String s = ls.get(i);
if("Meg".equals(s)){
ls.add("Tweek");
break;
}
}
System.out.println(ls);
}
}
控制台输出:
[Tweek, Meg, Chris, Peter, Louis, Tweek]
(2)OutOfMemoryError错误出现
注意:在对字符串常量调用equals函数作判断且找到当前元素后未执行break语句时,不能添加与当前字符串常量重复的元素,否则会使得在集合末尾不断自动扩容添加一个相同的元素,使得此时for循环进入死循环并出现OutOfMemoryError: Java heap space错误提示,这是因为JVM创建的对象太多了,在进行垃圾回收的过程中,JVM分配到堆内存空间已经用满了,与heap space有关。
下面的代码示例是出现OutOfMemoryError的代码示例:
/**
* @author Tweek
*/
public class ListDemo3 {
/**
出现OutOfMemoryError错误提示
*/
public static void main(String[] args) {
//创建List集合
List<String> ls = new ArrayList<>();
//添加元素
ls.add("Tweek");
ls.add("Meg");
ls.add("Chris");
ls.add("Peter");
ls.add("Louis");
//遍历集合的同时,添加元素
for (int i = 0; i < ls.size(); i++) {
String s = ls.get(i);
/**
* 此处进行equals元素比较的同时,添加了重复元素“Meg”导致OutOfMemoryError的出现
* 找到Meg元素后不执行break就会使得list末尾处不断自动扩容添加Meg,使得for循环进入死循环就会出现OutOfMemoryError
* 若执行break语句,则不会出现OutOfMemoryError错误提示
*/
if("Meg".equals(s)){
ls.add("Meg");
}
}
System.out.println(ls);
}
}
控制台输出:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at Collection.itemout.mList.ListDemo3.main(ListDemo3.java:32)
(3)并发修改异常产生的原因
在我们使用迭代器遍历集合的过程中,我们进行了集合add()方法添加元素的操作,导致next()方法内的变量modCount(实际修改次数)额外加一,使其与另一变量预期修改集合的次数expectedModCount不相等,致使next()方法内的checkForComodification方法内的modCount与expectedModCount判断为不相等,使其抛出并发修改异常ConcurrentModificationException
下面的代码示例是ConcurrentModificationException出现的源码分析:
public interface List<E>{
Iterator<E> iterator();
boolean add(E e);
}
public class AbstractList<E>{
protected int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
public Iterator<E> iterator() {
return new Itr();
}
/**
在get方法内没有做出checkForComodification的判断,所以不会抛出异常
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public boolean add(E e) {
/**
执行add操作,使得modCount额外加一
*/
modCount++;
add(e,elementData,size);
return true;
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
/**
modCount:实际修改集合的次数
expectedModCount:预期修改集合的次数
初始值都从0开始。
*/
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];
}
final void checkForComodification() {
/**
modCount与expectedModCount不相等时
抛出异常ConcurrentModificationException
*/
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
(4)列表迭代器(ListIterator)
ListIterator:列表迭代器
1.通过List集合的listiterator()方法得到,所以说它是List集合特有的迭代器
2、用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
3、列表迭代器的所有方法
方法 | 说明 |
---|---|
void add(E e) | 将指定的元素插入列表(可选操作) |
boolean hasNext() | 如果此列表迭代器在向前方向遍历列表时具有更多的元素,则返回true |
E next() | 返回列表中的下一个元素,并且前进光标位置 |
boolean hasPrevious() | 如果此列表迭代器在向后方向遍历列表时具有更多的元素,则返回true |
int nextIndex() | 返回由后续调用返回的元素的索引next() |
E previous() | 返回列表中的下一个元素,并且前进光标位置 |
int previousIndex() | 返回由后续调用返回的元素的索引previous() |
void remove() | 从列表中删除next()或previous()(可选操作)返回的最后一个元素 |
void set(E e) | 用指定的元素(可选操作)替换next()或previous()返回的最后一个元素 |
列表迭代器常用的方法:
方法 | 说明 |
---|---|
E next() | 返回迭代中的下一个元素 |
boolean hasNext() | 如果此列表迭代器在向前方向遍历列表时具有更多的元素,则返回true |
E previous() | 返回列表中的上一个元素 |
boolean hasPrevious() | 如果此列表迭代器在向后方向遍历列表时具有更多的元素,则返回true |
void add(E e) | 将指定元素插入列表 |
下面的代码示例,能让我们更好地理解列表迭代器的常用方法:
/**
* @author Tweek
*/
public class ListIteratorDemo {
public static void main(String[] args) {
/**
* ListIterator中常用的方法
* 1.E next()
* 2.boolean hasNext()
* 3.E previous()
* 4.boolean hasPrevious()
* 5.void add(E e)
*/
//创建List集合
List<String> ls = new ArrayList<>();
//添加元素
ls.add("Tweek");
ls.add("Meg");
ls.add("Chris");
ls.add("Peter");
ls.add("Louis");
//通过List集合的listiterator()方法获得
ListIterator<String> lit = ls.listIterator();
//1.E next() 2.boolean hasNext()
while(lit.hasNext()){
String s = lit.next();
System.out.println(s);
}
System.out.println("------------------");
//3.E previous() 4.boolean hasPrevious()
while(lit.hasPrevious()){
String s2 = lit.previous();
System.out.println(s2);
}
System.out.println("------------------");
//5.void add()
while(lit.hasNext()){
String s3 = lit.next();
if("Meg".equals(s3)){
lit.add("Biden");
//此处不用执行break语句,因为不同与迭代器Iterator的add方法使List自动扩容并把元素插入队尾,列表迭代器是插入指定E在当前if判断的字符串常量后面。
}
}
System.out.println(ls);
}
}
控制台输出:
Tweek
Meg
Chris
Peter
Louis
------------------
Louis
Peter
Chris
Meg
Tweek
------------------
[Tweek, Meg, Biden, Chris, Peter, Louis]
问题:为什么此处在遍历的同时,执行add操作不会发生并发修改异常ConcurrentModificationException呢?
回答:因为列表迭代器ListIterator的add()方法内,expectedModCount能更新出现修改后的modCount,故能始终保持相等,从而不会抛出并发修改异常ConcurrentModificationException
下面的代码示例,是以上问题的源码分析:
public interface List<E>{
Iterator<E> iterator();
ListIterator<E> listIterator();
}
public class AbstractList<E>{
protected int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
...
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
private class ListItr extends Itr implements ListIterator<E> {
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
//保证了这两个值一直相等,当modCount发生改变时,此处会使得expectedModCount更新与modCount相同的值,故不会造成并发修改异常
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
4、增强for循环
增强for:简化数组和Collection集合的遍历
(1)实现Iterable接口的类允许其对象成为增强型for语句的目标
(2)它是JDK5之后出现的,其内部原理是一个Iterator迭代器
(3)格式
for(元素数据类型变量名 : 数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
示例:
int[] arr = {1,2,3,4};
fpr(int i : arr){
System.out.println(i)
}
控制台输出:
1
2
3
4
下面的代码示例,能让我们更好地理解增强for循环的应用:
/**
* @author Tweek
*/
public class Form {
public static void main(String[] args) {
/**
* 增强for应用示例
* 1.数组
* 2.列表List
*/
String[] str = {"Tweek","Jim","Peter"};
for(String s:str){
System.out.println(s);
}
System.out.println("-------------------------");
List<String> ls = new ArrayList<>();
ls.add("Tweek");
ls.add("Peter");
ls.add("Jim");
for(String s:ls){
System.out.println(s);
}
System.out.println("-------------------------");
//内部原理是一个Iterator迭代器
//因为作为Iterator迭代器其在一个线程不允许修改集合的时候,另一个线程正在遍历。会抛出ConcurrentModificationException并发修改异常
for(String s:ls){
if("Peter".equals(s)){
ls.add("Meg");
}
}
}
}
控制台输出:
Tweek
Jim
Peter
-------------------------
Tweek
Peter
Jim
-------------------------
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at Collection.itemout.mList.Form.main(Form.java:27)
Process finished with exit code 1
5、List的遍历综合例子
创建一个存储学生对象的集合,存储5个学生对象,用程序实现在控制台的遍历。
下面是代码示例:
/**
* @author Tweek
*/
public class ListStudentsTest {
public static void main(String[] args) {
//创建学生类对象
Student stu1 = new Student("Tweek",16,100);
Student stu2 = new Student("Peter",32,54);
Student stu3 = new Student("Chris",17,65);
Student stu4 = new Student("Meg",17,0);
Student stu5 = new Student("Louis",32,87);
//创建List集合
List<Student> ls = new ArrayList<>();
ls.add(stu1);
ls.add(stu2);
ls.add(stu3);
ls.add(stu4);
ls.add(stu5);
//遍历集合,用迭代器的方法
System.out.println("遍历List集合,用迭代器");
Iterator<Student> it = ls.iterator();
while(it.hasNext()){
Student stu = it.next();
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("------------------------------------------");
//for循环方法
System.out.println("遍历List集合,用for循环");
for (int i = 0; i < ls.size(); i++) {
Student stu = ls.get(i);
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("------------------------------------------");
//增强for循环
System.out.println("遍历List集合,用增强for循环");
for(Student stu : ls){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
控制台输出:
遍历List集合,用迭代器
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
------------------------------------------
遍历List集合,用for循环
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
------------------------------------------
遍历List集合,用增强for循环
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
5、List集合子类的特点
List集合常用子类:ArrayList、LinkedList
特点:
(1)ArrayList
底层数据结构是数组,查询快,增删慢。因数组是按索引顺序存储的。
(2)LinkedList
底层数据结构是链表,查询慢,增删快。因链表是以指针来连接结点,增删只用增加或删除结点即可,而查询链表元素的话,需要遍历链表。
下面是代码示例,能让我们更好地了解List集合的子类
/**
* @author Tweek
*/
public class SonOfList {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> arr = new ArrayList<>();
arr.add("Tweek");
arr.add("Jim");
arr.add("Peter");
//遍历ArrayList
System.out.println("遍历ArrayList集合");
for(String s:arr){
System.out.println(s);
}
System.out.println("---------------------");
//创建LinkedList
LinkedList<String> lls = new LinkedList<>();
lls.add("Tweek");
lls.add("Jim");
lls.add("Peter");
System.out.println("遍历LinkedList集合");
for(String s: lls){
System.out.println(s);
}
}
}
控制台输出:
遍历ArrayList集合
Tweek
Jim
Peter
---------------------
遍历LinkedList集合
Tweek
Jim
Peter
(3)ArrayList遍历综合例子
创建一个存储学生对象的集合,存储5个学生对象,用程序实现在控制台的遍历。
下面是代码示例:
/**
* @author Tweek
*/
public class ArrayListStudentsTest {
public static void main(String[] args) {
/**
* 用三种方式遍历ArrayList
*/
//创建学生类对象
Student stu1 = new Student("Tweek",16,100);
Student stu2 = new Student("Peter",32,54);
Student stu3 = new Student("Chris",17,65);
Student stu4 = new Student("Meg",17,0);
Student stu5 = new Student("Louis",32,87);
//创建ArrayList集合
ArrayList<Student> arr = new ArrayList<>();
arr.add(stu1);
arr.add(stu2);
arr.add(stu3);
arr.add(stu4);
arr.add(stu5);
//1.迭代器遍历ArrayList
System.out.println("用迭代器遍历ArrayList");
Iterator<Student> it = arr.iterator();
while(it.hasNext()){
Student stu = it.next();
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("----------------------------");
//2.用普通for循环遍历ArrayList
System.out.println("用普通for循环遍历ArrayList");
for (int i = 0; i < arr.size(); i++) {
Student stu = arr.get(i);
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("----------------------------");
//3.用增强for循环遍历ArrayList
System.out.println("用增强for循环遍历ArrayList");
for(Student stu: arr){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
控制台输出:
用迭代器遍历ArrayList
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
----------------------------
用普通for循环遍历ArrayList
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
----------------------------
用增强for循环遍历ArrayList
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
(4)LinkedList集合的特有功能
LinkedList本质同ArrayList一样是一个迭代器
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFist() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
下面的代码示例,能让我们更好地理解LinkedList集合的特有功能
/**
* @author Tweek
*/
public class LinkedListCharacteristics {
/**
* LinkedList的常用方法
* | public void addFirst(E e) | 在该列表开头插入指定的元素 |
* | public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
* | public E getFirst() | 返回此列表中的第一个元素 |
* | public E getLast() | 返回此列表中的最后一个元素 |
* | public E removeFist() | 从此列表中删除并返回第一个元素 |
* | public E removeLast() | 从此列表中删除并返回最后一个元素 |
* @param args
*/
public static void main(String[] args) {
//创建LinkedList集合
LinkedList<String> lls = new LinkedList<>();
lls.add("Tweek");
lls.add("Jim");
lls.add("Peter");
lls.add("Jason");
lls.add("Louis");
//1.public void addFirst(E e)
System.out.print("添加元素前:");
System.out.println(lls);
System.out.println("addFirst():");
System.out.print("添加元素后: ");
lls.addFirst("Hello");
System.out.println(lls);
System.out.println("-------------------------");
//2.public void addLast(E e)
System.out.print("添加元素前:");
System.out.println(lls);
System.out.println("addLast():");
System.out.print("添加元素后: ");
lls.addLast("World");
System.out.println(lls);
System.out.println("-------------------------");
//3.public E getFirst();
System.out.println("getFirst: ");
System.out.print("获取第一个元素:");
System.out.println(lls.getFirst());
System.out.println("-------------------------");
//4.public E getLast();
System.out.println("getLast: ");
System.out.print("获取最后一个个元素:");
System.out.println(lls.getLast());
System.out.println("-------------------------");
//5.public E removeFirst()
System.out.print("删除第一个元素前: ");
System.out.println(lls);
System.out.print("删除第一个元素 "+lls.removeFirst()+" 后");
System.out.println(lls);
System.out.println("-------------------------");
//6.public E removeLast()
System.out.print("删除最后一个元素前: ");
System.out.println(lls);
System.out.print("删除最后一个元素 "+lls.removeLast()+" 后");
System.out.println(lls);
}
}
控制台输出:
添加元素前:[Tweek, Jim, Peter, Jason, Louis]
addFirst:
添加元素后: [Hello, Tweek, Jim, Peter, Jason, Louis]
-------------------------
添加元素前:[Hello, Tweek, Jim, Peter, Jason, Louis]
addLast:
添加元素后: [Hello, Tweek, Jim, Peter, Jason, Louis, World]
-------------------------
getFirst:
获取第一个元素:Hello
-------------------------
getLast:
获取最后一个个元素:World
-------------------------
删除第一个元素前: [Hello, Tweek, Jim, Peter, Jason, Louis, World]
删除第一个元素 Hello 后[Tweek, Jim, Peter, Jason, Louis, World]
-------------------------
删除最后一个元素前: [Tweek, Jim, Peter, Jason, Louis, World]
删除最后一个元素 World 后[Tweek, Jim, Peter, Jason, Louis]
(5)LinkedList遍历的综合例子
创建一个存储学生对象的集合,存储5个学生对象,用程序实现在控制台的遍历。
下面是代码示例:
/**
* @author Tweek
*/
public class LinkedListStudentsTest {
public static void main(String[] args) {
/**
* 用三种遍历方法遍历LinkedList
*/
//创建学生对象
Student stu1 = new Student("Tweek",16,100);
Student stu2 = new Student("Peter",32,54);
Student stu3 = new Student("Chris",17,65);
Student stu4 = new Student("Meg",17,0);
Student stu5 = new Student("Louis",32,87);
//创建LinkedList集合
LinkedList<Student> lls = new LinkedList<>();
lls.add(stu1);
lls.add(stu2);
lls.add(stu3);
lls.add(stu4);
lls.add(stu5);
//1.迭代器遍历LinkedList
System.out.println("用迭代器遍历LinkedList");
Iterator<Student> it = lls.iterator();
while(it.hasNext()){
Student stu = it.next();
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("----------------------------");
//2.用普通for循环遍历LinkedList
System.out.println("用普通for循环遍历LinkedList");
for (int i = 0; i < lls.size(); i++) {
Student stu = lls.get(i);
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
System.out.println("----------------------------");
//3.用增强for循环遍历LinkedList
System.out.println("用增强for循环遍历LinkedList");
for(Student stu: lls){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
控制台输出:
用迭代器遍历LinkedList
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
----------------------------
用普通for循环遍历LinkedList
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
----------------------------
用增强for循环遍历LinkedList
Name: Tweek Age: 16 Score: 100
Name: Peter Age: 32 Score: 54
Name: Chris Age: 17 Score: 65
Name: Meg Age: 17 Score: 0
Name: Louis Age: 32 Score: 87
四、Set集合
1、Set集合
Set是一个接口继承于Collection接口,它的功能全继承于Collection。Set的实现类:HashSet(对集合的迭代顺序不作任何保证,换句话说就是迭代输出顺序不按输入顺序)
2、Set集合特点
(1)不包含重复元素的集合
(2)没有带索引的方法,所以不能用普通for循环遍历,可以用增强for循环
下面的代码示例,能让我们更好地理解Set集合
/**
* @author Tweek
*/
public class SetDemo1 {
/**
* Set集合的特点
* 1.不包含重复元素的集合
* 2.没有带索引的方法,所以不能使用普通for循环遍历
*
* HashSet:对集合的迭代顺序不作任何的保证
*/
public static void main(String[] args) {
//创建集合对象
Set<String> st = new HashSet<>();
//添加元素
st.add("Tweek");
st.add("Jim");
st.add("Peter");
st.add("Louis");
//不包含重复的元素
st.add("Tweek");
//用增强for遍历
for(String s : st){
System.out.println(s);
}
}
}
控制台输出:
Peter
Tweek
Jim
Louis
3、Hash值
(1)哈希值
其是指JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
(2)哈希值获取方法
Object类中有一个方法可以获取对象的哈希值
public int hashCode():返回对象的哈希码值
(3)对象的哈希值的特点
1.同一个对象多次调用hashCode()方法返回的哈希值是相同的
2.默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同。其原理只有一个常量池,在常量池中已经创建好的常量地址不变,后边新创建的常量也只是引用了已经创建好的常量地址。
下面的代码示例,能让我们更好地了解hashCode的运作:
/**
* @author Tweek
*/
public class HashCodeDemo {
/**
* public int hashCode()
*/
public static void main(String[] args) {
Student stu1 = new Student("Tweek",15,87);
Student stu2 = new Student("Jim",16,58);
System.out.println("多次调用相同对象的hashCode:");
System.out.println("stu1 HashCode: "+stu1.hashCode());
System.out.println("stu1 HashCode: "+stu1.hashCode());
System.out.println("---------------------------------------------");
System.out.println("调用不同对象的hashCode:");
System.out.println("stu1 HashCode: "+stu1.hashCode());
System.out.println("stu2 HashCode: "+stu2.hashCode());
}
}
控制台输出:
多次调用相同对象的hashCode:
stu1 HashCode: 1163157884
stu1 HashCode: 1163157884
---------------------------------------------
调用不同对象的hashCode:
stu1 HashCode: 1163157884
stu2 HashCode: 1956725890
4、HashSet集合特点
(1)底层数据结构是哈希表
(2)对集合的迭代顺序不作任何的表征,也就是说不保证存储和取出的元素顺序一致
(3)没有带索引的方法,所以不能用普通for循环遍历
(4)由于是Set集合,所以是不包含重复元素的集合
下面的代码示例,能让我们更好地了解HashSet集合特点:
/**
* @author Tweek
*/
public class HashSetDemo1 {
/**
* (1)底层数据结构是哈希表
*
* (2)对集合的迭代顺序不作任何的表征,也就是说不保证存储和取出的元素顺序一致
*
* (3)没有带索引的方法,所以不能用普通for循环遍历
*
* (4)由于是Set集合,所以是不包含重复元素的集合
* @param args
*/
public static void main(String[] args) {
//创建HashSet集合对象
HashSet<String> hs = new HashSet<>();
//添加元素
hs.add("Tweek");
hs.add("Jim");
hs.add("Louis");
//不能包含重复元素
hs.add("Tweek");
//用增强for循环遍历
for(String s: hs){
System.out.println(s);
}
}
}
控制台输出:
Tweek
Jim
Louis
5、HashSet集合保证元素唯一性的源码分析
(1)原理
HashSet集合通过元素的哈希值是否相同的判断保证了元素的唯一性。首先调用对象的hasCode方法获取对象的哈希值,然后根据对象的哈希值计算对象的存储位置,此时判断该位置是否有元素存在,如果没有则将元素存储到该位置,反之如果有则遍历该位置的所有元素,和新存入的元素比较哈希值是否相同,此时若是都不相同,则可将元素存储到该位置,若是有相同的,则调用equals方法比较对象内容是否相等,如果不相等就返回false并将元素存储到该位置,如果相等则返回true并说明该元素已重复,集合不存储该元素。
下面的源码示例,能让我们更好地了解HashSet集合保证元素唯一性的原因
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//hash值和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果哈希表未初始化,就对其进行初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据对象的哈希值计算对象的储存位置,如果该位置没有元素,就储存元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*
如果该位置有元素
存入的元素和以前在这个位置的元素比较哈希值
如果哈希值不同,会继续往下执行,把元素添加到集合中
如果哈希值相同,会调用对象的equals()方法比较
如果返回false,会继续往下执行,把元素添加到集合
如果返回true,说明元素重复,不添加
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;//重复不添加
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);//添加了元素
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
(2)HashSet集合存储元素
要保证元素唯一性,需要重写hashCode()和equals()方法
重写equals方法就得重写hashCode方法,equals比较的是两个对象的地址,不同的对象是不同的地址,但是如果我们要集合中不出现相同内容的不同对象,那么就得重写equals方法使其比较对象中的内容,同理重写hashCode方法。
下面的两段代码示例,能让我们更好地理解为什么要重写hashCode()与equals()方法
代码1:
/**
* @author Tweek
*/
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
public class HashCodeDemo2 {
/**
* 未重写hashCode() and equals()
*/
public static void main(String[] args) {
//创建集合
HashSet<Student> hs = new HashSet<>();
Student stu1 = new Student("Tweek",15,100);
Student stu2 = new Student("Jim",15,87);
Student stu3 = new Student("Peter",15,88);
Student stu4 = new Student("Tweek",15,100);
hs.add(stu1);
hs.add(stu2);
hs.add(stu3);
hs.add(stu4);
//遍历
for(Student stu: hs){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
//此时出现两个相同内容的对象
控制台输出:
Name: Jim Age: 15 Score: 87
Name: Tweek Age: 15 Score: 100
Name: Tweek Age: 15 Score: 100
Name: Peter Age: 15 Score: 88
代码2:
/**
* @author Tweek
*/
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//以Student中的name比较hashCode
@Override
public int hashCode() {
return name.hashCode();
}
//重写equals的比较内容,以Student对象中的name比较
@Override
public boolean equals(Object obj) {
if(obj instanceof Student){
Student stu = (Student)obj;
return name.equals(stu.name);
}
return super.equals(obj);
}
}
public class HashCodeDemo2 {
/**
* 要保证元素唯一性,需要重写hashCode()和equals()方法
*/
public static void main(String[] args) {
//创建集合
HashSet<Student> hs = new HashSet<>();
Student stu1 = new Student("Tweek",15,100);
Student stu2 = new Student("Jim",15,87);
Student stu3 = new Student("Peter",15,88);
Student stu4 = new Student("Tweek",15,100);
hs.add(stu1);
hs.add(stu2);
hs.add(stu3);
hs.add(stu4);
//遍历
for(Student stu: hs){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
//没有相同内容的不同对象
控制台输出:
Name: Peter Age: 15 Score: 88
Name: Tweek Age: 15 Score: 100
Name: Jim Age: 15 Score: 87
6、LinkedHashCodeSet集合概述和特点
(1)概述
LinkedHashSet继承于HashSet且是Set的实现类,而且其底层数据结构是哈希表与链表,其具有可预测的迭代次序,换句话说,就是类似于FIFO队列,存储与取出的顺序一致。
(2)LinkedHashSet集合特点
1.哈希表和链表实现的Set接口,具有可预测的迭代次序
2.由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
3.由哈希表保证元素的唯一性,也就是说集合内没有重复的元素
下面的代码示例,能让我们更好地了解LinkedHashSet集合的特点
/**
* @author Tweek
*/
public class LinkedHashSetDemo1 {
/**
* 1.哈希表和链表实现的Set接口,具有可预测的迭代次序
*
* 2.由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
*
* 3.由哈希表保证元素的唯一性,也就是说集合内没有重复的元素
*
*/
public static void main(String[] args) {
//创建集合
LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("Tweek");
lhs.add("Jim");
lhs.add("Louis");
//不包含重复的元素
lhs.add("Tweek");
//遍历
for(String s : lhs){
System.out.println(s);
}
}
}
控制台输出:
Tweek
Jim
Louis
7、TreeSet集合概述和特点
(1)概述
TreeSet集合是一个具体的类其继承自AbstractSet,而且TreeSet实现了Set接口的实现类,即间接地实现了Set接口。在TreeSet内的元素使用自然排序natural Order(自然排序指字母表排序、阿拉伯数字排序)或比较器排序Comparator,使用哪种排序,取决于所使用的构造方法。
(2)TreeSet集合特点
1.元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于所使用的的构造方法,如下表
构造方法 | 说明 |
---|---|
TreeSet() | 根据其元素的自然排序进行排序 |
TreeSet(Comparator comparator) | 根据指定的比较器进行排序 |
2.没有带索引的方法,所以不能使用普通for循环遍历
3.由于是Set集合,所以不包含重复元素的集合
下面的代码示例,能让我们更好地了解TreeSet集合的特点
/**
* @author Tweek
*/
public class TreeSetDemo1 {
/**
* 1.元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于所使用的的构造方法
*
* 2.没有带索引的方法,所以不能使用普通for循环遍历
*
* 3.由于是Set集合,所以不包含重复元素的集合
*/
public static void main(String[] args) {
//自然排序
//创建集合
TreeSet<Integer> ts = new TreeSet<Integer>();
//添加元素
ts.add(10);
ts.add(50);
ts.add(40);
ts.add(20);
ts.add(30);
//不包含重复元素
ts.add(50);
//用增强for遍历
for(int i : ts){
System.out.println(i);
}
}
}
控制台输出:
10
20
30
40
50
(3)自然排序Comparable的使用
概述:Comparable接口可以对实现他的每个类的对象强加一个自然排序,故如果某个类要使用自然排序那么就得实现Comparable接口并重写compareTo方法按照需求重写其内容。
要求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序
下面是代码示例:
/**
* @author Tweek
*/
public class Student implements Comparable<Student>{
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
return age == student.age && score == student.score && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score);
}
@Override
public int compareTo(Student s) {
// return 0; 判断为两值相等
// return 1; 判断为升序
//return -1; 判断为降序
//升序
int num = this.age - s.age;
//判断年龄相同时,排序按名字字母自然排序
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
public class TreeSetDemo2 {
/**
* 要求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序
*/
public static void main(String[] args) {
//创建集合
TreeSet<Student> ts = new TreeSet<Student>();
Student stu1 = new Student("Tweek",15,100);
Student stu2 = new Student("Jim",15,87);
Student stu3 = new Student("Peter",15,88);
Student stu4 = new Student("Louis",16,77);
//添加对象
ts.add(stu1);
ts.add(stu2);
ts.add(stu3);
ts.add(stu4);
//遍历
for (Student stu : ts){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
控制台输出:
Name: Jim Age: 15 Score: 87
Name: Peter Age: 15 Score: 88
Name: Tweek Age: 15 Score: 100
Name: Louis Age: 16 Score: 77
(4)比较器比较Comparator的使用
概述:我们在使用Comparator接口时,应该明确的一点是我们需要Comparator接口的实现类对象,那么此时我们可以使用匿名内部类的方法,来使用Comparator的实现类对象。
要求:存储学生对象并遍历,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序
下面是代码示例:
/**
* @author Tweek
*/
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
return age == student.age && score == student.score && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score);
}
}
public class TreeSetDemo2 {
/**
* 要求:存储学生对象并遍历,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序
*/
public static void main(String[] args) {
//创建集合,使用匿名内部来使用Comparator
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge()-s2.getAge();
int num2 = num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
});
Student stu1 = new Student("Tweek",15,100);
Student stu2 = new Student("Jim",15,87);
Student stu3 = new Student("Peter",15,88);
Student stu4 = new Student("Louis",16,77);
//添加对象
ts.add(stu1);
ts.add(stu2);
ts.add(stu3);
ts.add(stu4);
//遍历
for (Student stu : ts){
System.out.println("Name: "+stu.getName()+" Age: " +
stu.getAge()+
" Score: "+stu.getScore());
}
}
}
控制台输出:
Name: Jim Age: 15 Score: 87
Name: Peter Age: 15 Score: 88
Name: Tweek Age: 15 Score: 100
Name: Louis Age: 16 Score: 77
五、泛型
1、泛型的概述和好处
(1)概述
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译到非法的类型。它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
参数化类型如何理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数类型可以用在类、方法、接口中,分别被称为泛型类、泛型方法,泛型接口
泛型定义格式:
1.<类型>:指定一种类型的格式,这里的类型可以看成是形参
2.<类型1,类型2....>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参。
3.将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型。
(2)泛型的好处
1.把运行期的问题提前到了编译时间
2.避免了强制类型转换
下面的两段代码比较,能让我们更好得理解泛型
/**
* @author Tweek
*/
public class GenericDemo1 {
/**
* Collection集合存储字符串并遍历
*/
public static void main(String[] args) {
//创建集合对象
//此时没有限定集合的参数类型,即此时集合的参数类型为Object
Collection c = new ArrayList();
//添加元素
c.add("Tweek");
c.add("Jim");
c.add("Louis");
c.add(100);
//遍历集合
for(Object o : c){
System.out.println(o);
}
}
}
控制台输出:
Tweek
Jim
Louis
100
代码2:
/**
* @author Tweek
*/
public class GenericDemo1 {
/**
* Collection集合存储字符串并遍历
*/
public static void main(String[] args) {
//创建集合对象
//此时没有限定集合的参数类型,即此时集合的参数类型为Object
Collection<String> c = new ArrayList<>();
//添加元素
c.add("Tweek");
c.add("Jim");
c.add("Louis");
//java: 不兼容的类型: int无法转换为java.lang.String
//c.add(100);
//遍历集合
for(String s : c){
System.out.println(s);
}
}
}
控制台输出:
Tweek
Jim
Louis
2、泛型类
(1)泛型类的定义格式
1.格式:修饰符class 类名<类型>{}
2、范例:public class Generic<T>{}
此处的T可以随便写为任意表示,常见的如T、E、K、V等形式的参数常用于表示泛型。
下面的代码示例,能让我们更好的了解泛型类
/**
* @author Tweek
*/
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
public class Teacher {
private String name;
private String subject;
public Teacher() {
}
public Teacher(String name, String subject) {
this.name = name;
this.subject = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
public class mGeneric<T> {
private T t ;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class GenericDemo2 {
public static void main(String[] args) {
//创建两个对象
Student stu = new Student("Tweek",15,87);
Teacher tch = new Teacher("Jim","English");
System.out.println(stu.getAge());
System.out.println(tch.getSubject());
System.out.println("-------------------------------------------");
//利用一个泛型类
mGeneric<Integer> g1= new mGeneric<Integer>();
mGeneric<String> g2= new mGeneric<String>();
g1.setT(15);
System.out.println(g1.getT());
g2.setT("English");
System.out.println(g2.getT());
}
}
控制台输出:
15
English
-------------------------------------------
15
English
3、泛型方法
(1)泛型方法的定义格式:
1.格式:修饰符<类型>返回值类型方法名(类型 变量名){}
2.范例:public<T> void show(T t){}
下面的代码示例,能让我们更好地了解泛型方法
/**
* @author Tweek
*/
public class mGeneric<T> {
public<T> void show(T t){
System.out.println(t);
}
}
public class GenericDemo2 {
public static void main(String[] args) {
mGeneric g = new mGeneric();
g.show("Tweek");
g.show(15);
g.show(true);
g.show(15.15);
}
}
控制台输出:
Tweek
15
true
15.15
4、泛型接口
(1)泛型接口的定义格式
1.格式:修饰符interface 接口名<类型>{ }
2.范例:public interface Generic<T>{ }
/**
* @author Tweek
*/
public interface mGenericInt<T> {
void show(T t);
}
public class mGenericImp<T> implements mGenericInt<T>{
@Override
public void show(T t) {
System.out.println(t);
}
}
public class GenericDemo2 {
public static void main(String[] args) {
mGenericImp g = new mGenericImp();
g.show("Tweek");
g.show(15);
g.show(true);
g.show(15.15);
}
}
控制台输出:
Tweek
15
true
15.15
5、类型通配符
(1)类型通配符概述
为了表示各种泛型List的父类,可以使用类型通配符
1.类型通配符: <?>
2.List<?> : 表示元素类型未知的List,它的元素可以匹配任何类型
3.这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限
4.类型通配符的上限:<? extends 类型>
5.List<? extends Number>:表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,我们还可以指定类型通配符的下限
6.类型通配符的下限:<? super 类型>
7.List<? super Number>:表示的类型是Number或者其父类型
下面的代码,能让我们更好地理解类型通配符
/**
* @author Tweek
*/
public class GenericDemo3 {
public static void main(String[] args) {
//类型通配符,Object是Number的父类,Number是Integer的父类
List<?> ls1 = new ArrayList<Object>();
List<?> ls2 = new ArrayList<Number>();
List<?> ls3 = new ArrayList<Integer>();
System.out.println("----------");
//类型通配符上限:<? extends 类型>
//Number的上限是它自己,所以不能创建Object类型对象
//List<? extends Number> ls4 = new ArrayList<Object>();
List<? extends Number> ls5 = new ArrayList<Number>();
List<? extends Number> ls6 = new ArrayList<Integer>();
System.out.println("----------");
//类型通配符下限:<? super 类型>
//Number的下限是它自己,所以不能创建其子类Integer类型的对象
List<? super Number> ls7 =new ArrayList<Object>();
List<? super Number> ls8 =new ArrayList<Number>();
//List<? super Number> ls9 =new ArrayList<Integer>();
}
}
6、可变参数
(1)格式:修饰符 返回值类型 方法名(数据类型...变量名){ }
(2)范例:public static int sum(int... a){ }
对于int... a 其实是把方法的数据封装成了一个数组a
(3)注意事项:
1.这里的变量其实是一个数组
2.如果一个方法有多个参数,包含可变参数,可变参数要放在最后
下面的代码示例,能让我们更好地了解可变参数:
/**
* @author Tweek
*/
public class ArgsDemo1 {
public static void main(String[] args) {
System.out.println(sum(10,20));
System.out.println(sum(10,20,30));
System.out.println(sum(10,20,30,40));
System.out.println(sum(10,20,30,40,50,60));
System.out.println(sum(10,20,30,40,50,60,70));
}
private static int sum(int... a) {
int sum=0;
System.out.println(a);
for(int i : a){
sum+=i;
}
return sum;
}
}
控制台输出:
30
60
100
210
280
7、可变参数的使用
注:这list.of是JDK9的新特性
方法名 | 说明 |
---|---|
Arrays工具类中有一个静态方法(返回的集合不能做增删操作,可以做修改操作) | |
public static <T> List<T> asList(T... a) | 返回指定数组支持的固定大小的列表 |
List接口中有一个静态方法(返回的集合不能做增删改操作) | |
public static <E> List <E> of(E... elements) | 返回包含任意数量元素的不可变列表 |
Set接口中有一个静态方法(返回的集合不能做增删操作,没有修改的方法) | |
public static <E> Set <E> of(E... elements) | 返回一个包含任意数量元素的不可变集合 |
六、Map集合
1、Map集合的概述与特点
(1)概述
Map集合是一个接口,其形式是Map<K、V>,其中K是Map里面存储的键的类型,V是Map里面存储的值的类型。Map接口是一个将键K映射到值V的对象,Map中不能包含重复的键K,每个键K可以映射最多一个值V
创建Map集合的对象:采用多态的方式通过具体的实现类HashMap来创建Map集合的对象
(2)特点
1、Map集合中不包含重复的元素,在出现两个相同K,但是不相同V的时候,Map集合会将相同K中的旧元素删去,更新K对应的V是新元素。
2、Map集合的输出顺序,不是按存储顺序输出。
下面的代码示例,能让我们更好地了解Map集合
/**
* @author Tweek
*/
public class MapDemo1 {
/**
* Map集合
* @param args
*/
public static void main(String[] args) {
//创建集合对象
Map<String,String> mp = new HashMap<String , String >();
//V put(K key , V value) 将指定的值与该映射中的指定键相关联
mp.put("ID1515","Tweek");
mp.put("ID1516","Jim");
mp.put("ID1517","Peter");
//不包含重复元素。若是相同的K,但是不同的内容,Map集合会将相同K中的旧元素删去,更新元素
mp.put("ID1517","Louis");
//输出集合
System.out.println(mp);
}
}
控制台输出:
{ID1517=Louis, ID1516=Jim, ID1515=Tweek}
2、Map集合的基本功能
方法名 | 说明 |
---|---|
Vput(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containKey(Object key) | 判断集合是否包含指定的键 |
bool containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
下面的代码示例,能让我们更好地了解Map集合的基本功能
/**
* @author Tweek
*/
public class MapDemo2 {
/**
* | V put(K key,V value) | 添加元素 |
* | V remove(Object key) | 根据键删除键值对元素 |
* | void clear() | 移除所有的键值对元素 |
* | boolean containKey(Object key) | 判断集合是否包含指定的键 |
* | bool containsValue(Object value) | 判断集合是否包含指定的值 |
* | boolean isEmpty() | 判断集合是否为空 |
* | int size() | 集合的长度,也就是集合中键值对的个数 |
*/
public static void main(String[] args) {
//创建集合
Map<String,String> map = new HashMap<>();
//1.V put(K key,V value)
System.out.print("添加元素:");
map.put("ID1","Tweek");
map.put("ID2","Jim");
map.put("ID3","Peter");
map.put("ID4","Meg");
System.out.println(map);
System.out.println("----------------------------");
//2.V remove(Object key)
System.out.print("移除元素前:");
System.out.println(map);
System.out.print("移除ID4的"+map.remove("ID4")+"元素后:");
System.out.println(map);
System.out.println("----------------------------");
//3.boolean containKey(Object key)
System.out.println("判断是否存在这个Key值:");
System.out.println("是否存在ID1这个Key?true存在,false不存在");
System.out.println("回答: "+map.containsKey("ID1"));
System.out.println("是否存在ID5这个Key?true存在,false不存在");
System.out.println("回答: "+map.containsKey("ID5"));
System.out.println("----------------------------");
//4.bool containsValue(Object value)
System.out.println("判断是否存在这个Value值:");
System.out.println("是否存在Tweek这个Value?true存在,false不存在");
System.out.println("回答: "+map.containsValue("Tweek"));
System.out.println("是否存在Meg这个Value?true存在,false不存在");
System.out.println("回答: "+map.containsValue("Meg"));
System.out.println("----------------------------");
//5.boolean isEmpty()
System.out.println("判断该集合是否为空");
System.out.println("为空true,不为空则false");
System.out.println("回答:"+map.isEmpty());
System.out.println("查看集合:"+map);
System.out.println("----------------------------");
//6.int size()
System.out.println("该集合有多长?");
System.out.println("回答:长度为"+map.size());
System.out.println("----------------------------");
//7. void clear()
System.out.println("清空集合前:"+map);
map.clear();
System.out.println("判断集合是否为空:isEmpty?"+map.isEmpty());
System.out.println("清空集合后:"+map);
}
}
控制台输出:
添加元素:{ID2=Jim, ID1=Tweek, ID4=Meg, ID3=Peter}
----------------------------
移除元素前:{ID2=Jim, ID1=Tweek, ID4=Meg, ID3=Peter}
移除ID4的Meg元素后:{ID2=Jim, ID1=Tweek, ID3=Peter}
----------------------------
判断是否存在这个Key值:
是否存在ID1这个Key?true存在,false不存在
回答: true
是否存在ID5这个Key?true存在,false不存在
回答: false
----------------------------
判断是否存在这个Value值:
是否存在Tweek这个Value?true存在,false不存在
回答: true
是否存在Meg这个Value?true存在,false不存在
回答: false
----------------------------
判断该集合是否为空
为空true,不为空则false
回答:false
查看集合:{ID2=Jim, ID1=Tweek, ID3=Peter}
----------------------------
该集合有多长?
回答:长度为3
----------------------------
清空集合前:{ID2=Jim, ID1=Tweek, ID3=Peter}
判断集合是否为空:isEmpty?true
清空集合后:{}
3、Map集合的获取功能
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set<K> keySet() | 获取所有键的集合 |
Collection<V> values() | 获取所有值的集合 |
Set<Map.Enty<K,V>> entrySet() | 获取所有键值对对象的集合 |
下面的代码示例,能让我们更好地了解Map集合的获取功能
/**
* @author Tweek
*/
public class MapDemo3 {
/**
* | V get(Object key) | 根据键获取值 |
* | Set<K> keySet() | 获取所有键的集合 |
* | Collection<V> values() | 获取所有值的集合 |
* | Set<Map.Enty<K,V>> entrySet() | 获取所有键值对对象的集合 |
*/
public static void main(String[] args) {
//创建集合
Map<String,String> map = new HashMap<>();
//添加元素
map.put("ID1","Tweek");
map.put("ID2","Jim");
map.put("ID3","Peter");
map.put("ID4","Meg");
System.out.println(map);
System.out.println("----------------------------");
//1.根据键获取value,如果对应的键值不存在,则值返回null
System.out.println("根据键获取value");
System.out.println(map.get("ID1"));
System.out.println(map.get("ID5"));
//2.获取所有键的集合
System.out.println("获取所有键的集合");
//创建集合
Set<String> keySet = map.keySet();
for(String s : keySet){
System.out.println(s);
}
System.out.println("----------------------------");
//3.获取所有值的集合
System.out.println("获取所有值的集合");
//创建集合
Collection<String> valueSet = map.values();
for(String s:valueSet){
System.out.println(s);
}
System.out.println("----------------------------");
//4.获取所有键值对对象的集合
System.out.println("获取所有键值对的集合");
//创建集合
Set<Map.Entry<String,String>> entrySet = map.entrySet();
for(Map.Entry<String,String> s:entrySet){
System.out.println(s);
}
}
}
控制台输出:
{ID2=Jim, ID1=Tweek, ID4=Meg, ID3=Peter}
----------------------------
根据键获取value
Tweek
null
获取所有键的集合
ID2
ID1
ID4
ID3
----------------------------
获取所有值的集合
Jim
Tweek
Meg
Peter
----------------------------
获取所有键值对的集合
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter
4、Map的遍历方法
方式一
获取所有键的集合,用keySet()方法实现
(1)用增强for循环遍历
1.遍历键的集合,获取到每一个键,用增强for实现
2.根据键去找值,用get(Object key)来根据键获取值
(2)用迭代器遍历
1.遍历键的集合,获取每一个键,用迭代器的方法来实现
2.根据键去找值,用get(Object key)来根据键获取值
下面的代码示例,能让我们更好地了解Map遍历方法
/**
* @author Tweek
*/
public class MapDemo4 {
public static void main(String[] args) {
/**
* Map的遍历方法
* 1.获取所有键的集合,用keySet()方法实现
*
* (1)遍历键的集合,获取到每一个键,用增强for实现
*
* (2)根据键去找值,用get(Object key)来根据键获取值
* 2.用迭代器遍历
*/
//创建集合
Map<String,String> map = new HashMap<>();
//添加元素
map.put("ID1","Tweek");
map.put("ID2","Jim");
map.put("ID3","Peter");
map.put("ID4","Meg");
//1.获取所有键的集合,用KeySet方法实现
Set<String> keySet = map.keySet();
for(String s : keySet){
String key = s;
String value = map.get(key);
System.out.println(key+"="+value);
}
System.out.println("----------------------");
//2.用迭代器遍历
Set<String> itkey = map.keySet();
Iterator<String> it = itkey.iterator();
while(it.hasNext()){
String key = it.next();
String value = map.get(key);
System.out.println(key+"="+value);
}
}
}
控制台输出:
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter
----------------------
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter
方式二
根据键值对对象来找键和值
(1)用增强for来遍历
1.获取所有的键值对的集合
2.遍历包含所有键值对的Set集合,得到每一个键值对对象
3.根据获取到的每一个键值对,来获取键和值
(2)用迭代器遍历
1.获取所有的键值对的集合
2.遍历包含所有键值对的Set集合,得到每一个键值对对象
3.根据获取到的每一个键值对,来获取键和值
下面的代码示例,能让我们更好地了解Map遍历方法
/**
* @author Tweek
*/
public class MapDemo5 {
/**
* 根据键值对对象来找键和值
*
* (1)用增强for来遍历
*
* 1.获取所有的键值对的集合
*
* 2.遍历包含所有键值对的Set集合,得到每一个键值对对象
*
* 3.根据获取到的每一个键值对,来获取键和值
*
* (2)用迭代器遍历
*/
public static void main(String[] args) {
//创建集合
Map<String,String> map = new HashMap<String,String>();
//添加元素
map.put("ID1","Tweek");
map.put("ID2","Jim");
map.put("ID3","Peter");
map.put("ID4","Meg");
//遍历集合
Set<Map.Entry<String,String>> entrySet = map.entrySet();
//1.增强for
for(Map.Entry<String,String> s : entrySet){
String key = s.getKey();
String value = s.getValue();
System.out.println(key+"="+value);
}
System.out.println("----------------------");
//2.迭代器遍历
Iterator<Map.Entry<String,String>> it =entrySet.iterator();
while(it.hasNext()){
Map.Entry<String,String> entry=it.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
}
}
控制台输出:
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter
----------------------
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter