集合-Collection接口
集合 和 数组 的比较:
数组 - 本质上就是在内存空间中申请的一段连续内存空间,存放多个相同类型的数据
- 数组一旦定义完毕,则在内存空间中的长度固定。
- 插入/删除元素时可能导致大量元素的移动,因此效率比较低。
- 使用数组下标访问元素非常便利。
- 数组中的元素可以是基本数据类型,也可以是引用数据类型。
集合 - 内存空间不一定连续,数据类型不一定相同。
- 内存空间的长度不固定,可以动态调整。
- 插入/删除元素时可以不移动大量元素,效率可以提高。
- 不一定支持下标访问。
- 集合中的所有元素都必须是引用数据类型,(如果硬是要放置基本数据类型,则借助包装类。)
集合 主要分为两大类: Collection 接口 和 Map接口
其中
Collection 接口中 存放元素的基本单位是 单个元素 (单列集合)
Map 接口中 存放元素的基本单位是 单对元素 (双列集合)
Collection 接口本身很少使用,通常都是使用 子接口: List接口、Set接口、Queue接口
注意事项:
集合中只能存储引用类型;
集合之所以能够存放各种引用类型的数据,是因为:
都当做Object类型处理。(多态方式放置)
即,往集合中放入任何一个元素的时候,其实都是提升为一个Object类型,存储进集合中。
一、Collection接口:
常用方法:
boolean add(E e) -用于将参数指定的元素放入当前集合中。若当前集合发生了更改,则返回true,否则返回false
boolean addAll(Collection<? extends E> c) -用于将参数指定集合中的所有元素都增加到当前集合中;
boolean contains (Object o) -判断参数指定的元素是否在当前集合中
boolean containsAll(Collection<?> c) -判断参数指定集合中的所有元素是否在当前集合中
boolean remove(Object o) -用于删除参数指定的单个元素
boolean removeAll(Collection<?> c) -删除参数指定的所有元素
void clear() -用于清空当前的集合
boolean isEmpty() -判断当前集合是否为空
int size() -用于获取当前集合中的元素个数
boolean retainAll(Collection<?> c) -用于计算两个集合的交集
Iterator<E> iterator() -用于获取当前集合中的迭代器
代码:
1.写一个Student类 (int id,String name) 这里省略不写了
2. 测试
package com.monkey1024; import java.util.ArrayList; import java.util.Collection; public class CollectionTest { public static void main(String[] args) { // new c1ollec1tion(); // 报错原因:c1ollec1tion是个接口,接口不能构造对象 //c1ollec1tion接口的引用 指向 实现类的对象,形成多态 Collection c1 = new ArrayList(); // 向集合中,添加String类型的元素 boolean b = c1.add("one"); System.out.println("向c1集合中添加String类型的元素\"one\"是否成功: "+b); b = c1.add("one"); System.out.println("再次向c1集合中添加一个String类型的元素\"one\"是否成功: "+b); // 向集合中添加int基本数据类型的数据为什么不报错? // 实现了自动装箱处理:int ===> Integer类型 c1.add(23); // 向集合中添加了Student类型的数据 c1.add(new Student(1001,"James")); // 打印集合中的每个元素,自动调用toString()方法 System.out.println("打印集合c1中的元素:"+c1); // 打印集合中元素的个数 int num = c1.size(); System.out.println("集合c1中元素个数: "+num); System.out.println(); // 准备另一个新集合c2 Collection c2 =new ArrayList(); c2.add(3); c2.add("four"); c2.add("two"); // 查看c2集合 System.out.println(c2); // 将当前c2集合中的所有元素都放入到c1集合中 b = c1.addAll(c2); System.out.println("将当前c2集合中的所有元素都放入到c1集合中: "+b); System.out.println("打印c1集合:"+c1); System.out.println(); // 判断c1集合中是否有元素"one" b = c1.contains("one"); System.out.println("c1集合中是否有元素\"one\": "+b); // 判断集合是否存在的本质就是 调用了equals(),不重写该方法就比较对象的地址。 b = c1.contains(new Student(1001,"James")); System.out.println("判断给集合中是否存在Student(1001,\"James\")对象: "+b); // 判断c1集合中是否包含c2集合 b = c1.containsAll(c2); System.out.println("集合c1中是否包含集合c2: "+b); b = c2.add("5"); System.out.println("集合c2中添加元素5: "+b); // 判断c1集合中是否包含c2集合 b = c1.containsAll(c2); System.out.println("集合c1中是否包含集合c2: "+b); System.out.println(); // 删除集合c1中的元素"one" b = c1.remove("one"); System.out.println("删除集合c1中的元素\"one\": "+b); // 删除集合c1中的元素28 b = c1.remove(28); System.out.println("删除集合c1中的元素28: "+b); // 删除集合c1中包含的集合c2 b = c1.removeAll(c2); System.out.println("删除集合c1中包含的集合c2: "+b); // 清空集合c1中所有元素 c1.clear(); // 打印集合c1 System.out.println("打印集合c1: "+c1); // 创建一个集合c3 Collection c3 = new ArrayList(); c3.add(23); c3.add(24); c3.add(25); // 创建一个集合c4 Collection c4 = new ArrayList(); c4.add(25); c4.add(26); c4.add(27); // 计算集合c3 和 集合 c4的交集,若有交集,则把交集部分保留在c3中,并且返回true。否则返回false b = c3.retainAll(c4); System.out.println("集合c3和集合是否有交集: "+b); System.out.println("打印集合c3:"+c3); // 当集合自己和自己取交集时,则交集还是自己,自己不会发生改变,返回false b = c4.retainAll(c4); System.out.println("集合c4自己和自己取交集:"+b); System.out.println("打印集合c4:"+c4); // 清空集合c4中的元素 c4.clear(); // 判断集合c4是否为空集合 b = c4.isEmpty(); System.out.println("打印集合c4: "+c4); System.out.println("对集合c4进行了清空操作后,判断集合c4是否为空集合: "+b); } }
结果:
向c1集合中添加String类型的元素"one"是否成功: true 再次向c1集合中添加一个String类型的元素"one"是否成功: true 打印集合c1中的元素:[one, one, 23, Student [name=James, id=1001]] 集合c1中元素个数: 4 [3, four, two] 将当前c2集合中的所有元素都放入到c1集合中: true 打印c1集合:[one, one, 23, Student [name=James, id=1001], 3, four, two] c1集合中是否有元素"one": true 判断给集合中是否存在Student(1001,"James")对象: true 集合c1中是否包含集合c2: true 集合c2中添加元素5: true 集合c1中是否包含集合c2: false 删除集合c1中的元素"one": true 删除集合c1中的元素28: false 删除集合c1中包含的集合c2: true 打印集合c1: [] 集合c3和集合是否有交集: true 打印集合c3:[25] 集合c4自己和自己取交集:false 打印集合c4:[25, 26, 27] 打印集合c4: [] 对集合c4进行了清空操作后,判断集合c4是否为空集合: true
二、 List 接口
1.概述
java.util.List接口是Collection接口的子接口,继承自Collection接口。
该集合中的元素是有序的(放入次序,而非大小次序),允许有重复的元素存在。
该接口的主要实现类:ArrayList类、LinkedList类、Stack类、Vector类(笔试)
ArrayList类的底层 --- 动态数组 实现 访问 和 修改 方 便, 增删不方便;
LinkedList类的底层 --- 链 表 实现 访问 和 修改 不方便,增删方便;
Stack类的底层 --- 动态数组 实现 该类是一种具有后进先出特性的数据结构,简称LIFO [ last in first out ] 栈 ;
Vector类的底层 --- 动态数组 实现 与ArrayList类相比,支持线程安全,效率比较低。Java官方推荐使用ArrayList来取代它。
2.常用方法 (List接口中的方法包含了Collection接口中的方法)
void add(int index, E element) -将数据element插入到 index指向的位置
boolean addAll(int index, Collection<? extends E> c) -将集合c中的所有元素插入到index指向的位置
E remove(int index) -删除index指向位置的元素,并返回该位置被删除的元素
E set(int index, E element) -将index位置的元素替换为element,返回替换之前的元素值。 index的取值范围时:[ 0, size()-1 ]
E get(int index) -返回index指向位置的元素值
List<E> subList(int fromIndex, int toIndex) -获取集合中下标从fromIndex(包含) 到toIndex(不包含)之间的所有元素
代码:
package com.monkey1024.collection; import java.util.ArrayList; //List接口 import java.util.List; import com.monkey1024.bean.Student; public class ListTest { public static void main(String[] args) { // 定义接口类型的引用,指向实现类的对象 形成多态 List l1=new ArrayList(); // 操作1:向集合中添加元素 l1.add("one"); l1.add(123); l1.add(new Student(29,"郜林")); l1.add(123); // 操作2:向指定的下标位置 插入元素 (索引的方式) l1.add(0, "first"); l1.add(2,2019); System.out.println("打印集合l1: "+l1); System.out.println(); // l1.add(9,2019); 报数组下标越界异常 System.out.println(l1); // 操作3:将l2集合中的所有元素 插入到l1集合中 下标为1的位置 List l2 =new ArrayList(); l2.add("two"); l2.add(1001); l2.add(new Student(23,"迪亚曼蒂")); l2.add(1001); boolean b = l1.addAll(1, l2); System.out.println("将l2集合中的所有元素 插入到l1集合中 下标为1的位置: "+b); System.out.println("打印集合l1: "+l1); System.out.println(""); // 操作4:获取 l1集合中下标为 3的元素 //String str = (String) l1.get(3); // error 报错原因:所有放入元素都是object类型放入的 //System.out.println(str); //解决方案:做判断 //不论元素是以什么类型存入集合中的,从集合中取出的元素都是Object类型 Object obj = l1.get(3); if(obj instanceof String ) { String str = (String)obj; System.out.println("打印集合l1中下标为3的元素: l1[3] ="+str); }else { System.out.println(obj); } System.out.println( ); // 操作5:获取l1集合中的所有元素值 (要求每个元素单独占一行) System.out.println("获取l1集合中的所有元素值 (要求每个元素单独占一行):"); for(int i=0;i<l1.size();i++) { // 获取集合中的每一个元素 System.out.println(l1.get(i)); } System.out.println( ); // 操作6:删除 l1集合中,下标为2的元素 Object remove = l1.remove(2); System.out.println("打印被删除的数据值: "+remove); //返回值是 被删除的数据值 System.out.println(); //操作7: 将l1集合中下标为1的元素 改为 "two" obj=l1.set(1, "what"); System.out.println(obj); //返回值是 修改之前的数据值 two System.out.println( ); // 操作8:获取 链表中 指定区间位置的所有元素 (操作的是同一块内存空间) List l3 = l1.subList(1, 4); //从索引1开始,但是不包含索引4 System.out.println("打印l3: "+l3); System.out.println( ); // 操作9:泛型的使用 //限制了该集合中 只能存放String类型 List<String> l4 = new ArrayList<String> (); l4.add("霍霍托夫斯基"); // l4.add(111); 报错 111是int类型 // l4.add(new Student(07,"C罗")); 报错 这是 Student类型 System.out.println(l4); } }
结果:
打印集合l1: [first, one, 2019, 123, Student[id=29,name+郜林], 123] [first, one, 2019, 123, Student[id=29,name+郜林], 123] 将l2集合中的所有元素 插入到l1集合中 下标为1的位置: true 打印集合l1: [first, two, 1001, Student[id=23,name+迪亚曼蒂], 1001, one, 2019, 123, Student[id=29,name+郜林], 123] Student[id=23,name+迪亚曼蒂] 获取l1集合中的所有元素值 (要求每个元素单独占一行): first two 1001 Student[id=23,name+迪亚曼蒂] 1001 one 2019 123 Student[id=29,name+郜林] 123 打印被删除的数据值: 1001 two [what, Student[id=23,name+迪亚曼蒂], 1001] [霍霍托夫斯基]
三、Queue 接口
import java.util.LinkedList; import java.util.Queue; public class TestQueue { public static void main(String[] args) { // 定义一个 Queue接口类型的引用指向 实现类的对象,形成了多态 // 使用了泛型,规定该集合类只能存储Integer类型的元素 Queue<Integer> q1 =new LinkedList<Integer>(); // 操作1:向集合中添加元素 for(int i=1;i<7;i++) { q1.offer(i*10+i); } System.out.println("1.打印集合q1的元素: "+q1); // 操作2:获取队列中首元素值 并且打印 Integer peek = q1.peek(); System.out.println("2.获取队列中首元素值: "+peek); // 操作3: 删除集合中的元素 for(int i=1;i<7;i++) { System.out.println("3.执行了:删除集合q1中的元素 "+q1.poll()); } // 操作4:获取队列中首元素并打印 peek = q1.peek(); System.out.println("4.获取队列中首元素并打印: "+peek); } }
结果:
1.打印集合q1的元素: [11, 22, 33, 44, 55, 66] 2.获取队列中首元素值: 11 3.执行了:删除集合q1中的元素11 3.执行了:删除集合q1中的元素22 3.执行了:删除集合q1中的元素33 3.执行了:删除集合q1中的元素44 3.执行了:删除集合q1中的元素55 3.执行了:删除集合q1中的元素66 4.获取队列中首元素并打印: null
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class TestSet {
public static void main(String[] args) {
// 定义一个Set类型的接口 的引用 指向 实现类的对象 形成多态
Set<String> s1 =new HashSet<String> ();
// 往 集合中添加元素
s1.add("one");
s1.add("two");
s1.add("three");
// 操作1: 添加重复的元素
boolean b = s1.add("one");
System.out.println("1.添加重复的元素能否成功:"+b); // 返回false 重复的元素添加失败
System.out.println();
// 操作2: 打印集合中的所有元素,整体 返回字符串,操作不够灵活
System.out.println("2.打印集合s1: "+s1);
System.out.println();
// 操作3: 使用迭代器打印集合中的元素
// 获取 迭代器对象
Iterator<String> it = s1.iterator();
// 判断是否有可以 迭代的元素,有则取出并打印
while(it.hasNext()) {
String str = it.next();
System.out.println("3.使用迭代器打印集合中的元素: "+str);
}
// 操作4: 删除集合中的元素two
s1.remove("two");
System.out.println("4.删除元素two之后的s1集合:"+s1);
// 重新获取集合中的迭代器!!!!
it=s1.iterator();
// 操作5:将集合中使用 "t" 开头的元素 找到并删除
while(it.hasNext()) {
if(it.next().startsWith("t")) {
// 引发对象并发修改异常,集合在迭代的过程中不允许修改
// s1.remove(it.next()); // 引发对象并发修改异常,因为集合在迭代的过程中不允许修改
it.remove(); // 只能用迭代器自己的remove()方法来删除刚刚遍历的元素
}
}System.out.println("5.打印集合s1: "+s1);
}
}
1.添加重复的元素能否成功:false
2.打印集合s1: [one, two, three]
3.使用迭代器打印集合中的元素: one
3.使用迭代器打印集合中的元素: two
3.使用迭代器打印集合中的元素: three
4.删除元素two之后的s1集合:[one, three]
5.打印集合s1: [one]
代码:
package com.monkey1024.collection; import java.util.Iterator; import java.util.TreeSet; public class TestTreeSet { public static void main(String[] args) { // 定义接口类型的引用 指向 实现类的对象,形成多态 TreeSet<Integer> s1 = new TreeSet<Integer> (); // 增加元素到集合中 s1.add(30); s1.add(10); s1.add(20); // 要求:打印集合中的所有元素 // 有3种方式: // toString()、迭代器、foreach // 01.按照升序方式打印 (中序遍历方式) System.out.println("1.按照升序方式打印: "+s1); System.out.println(); // 02.使用迭代器打印 Iterator<Integer> iterator = s1.iterator(); // 获得迭代器对象 while(iterator.hasNext()){ // 判断该集合有无可迭代的元素 Integer next = iterator.next(); System.out.println("2.使用迭代器打印:"+next); } System.out.println(); // 03.使用foreach(增强for循环) 打印 for (Integer it : s1) { System.out.println("3.使用foreach打印: "+it); } } }
结果:
1.按照升序方式打印: [10, 20, 30]
2.使用迭代器打印:10
2.使用迭代器打印:20
2.使用迭代器打印:30
3.使用foreach打印: 10
3.使用foreach打印: 20
3.使用foreach打印: 30
练习:
自定义一个Student类实现封装;
在StudentTest测试类中定义TreeSet集合,并且向该集合中放入3个学生信息打印结果
1.Student类
package com.moneky1024; public class Student implements Comparable<Student>{ private int id ; private String name; public Student(int id, String name) { super(); setId(id); setName(name); } public int getId() { return id; } public void setId(int id) { if(id>0){ this.id = id; }else { System.out.println("学号不合理......."); } } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (id != other.id) return false; return true; } // 实现了Comparable<String>接口,则必须重写compareTo(String o)方法 public int compareTo(Student o) { // 按照学号比较大小 //return getId()-o.getId(); // 按照姓名比较大小 // 调用compareTo()方法, 属性name 跟 参数name 比较 return getName().compareTo(o.getName()); } }
测试类
import java.util.Comparator; import java.util.Set; import java.util.TreeSet; public class Test { public static void main(String[] args) { Set<Student> s = new TreeSet<Student>(); // 向集合s 中添加3个学生信息 s.add(new Student(1001,"Mary")); s.add(new Student(1002,"Bon")); s.add(new Student(1003,"Wide")); for (Student stu : s) { System.out.println("按照姓名进行排序打印:"+stu); } System.out.println("------------------------------------------------"); // 先自定义比较器,使用 比较器指定的规则进行排序。那么使用有参的构造方法 Set<Student> s2 = new TreeSet<Student>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // 按照学号比较元素大小 return o1.getId()-o2.getId(); } }); s2.add(new Student(1001,"Mary")); s2.add(new Student(1002,"Bon")); s2.add(new Student(1003,"Wide")); for (Student stu2 : s2) { System.out.println("按照学号进行排序的结果: "+stu2); // 按照学号进行排序的结果 } System.out.println("--------------------------------------------------"); Set<Student> s3 = new TreeSet<Student>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // 按照姓名比较元素的大小 return o1.getName().compareTo(o2.getName()); } }); s3.add(new Student(1001,"Mary")); s3.add(new Student(1002,"Bon")); s3.add(new Student(1003,"Wide")); for (Student student : s3) { System.out.println("按照姓名比较元素的大小: "+student); } } }
显示结果:
按照姓名进行排序打印:Student [id=1002, name=Bon] 按照姓名进行排序打印:Student [id=1001, name=Mary] 按照姓名进行排序打印:Student [id=1003, name=Wide] ------------------------------------------------ 按照学号进行排序的结果: Student [id=1001, name=Mary] 按照学号进行排序的结果: Student [id=1002, name=Bon] 按照学号进行排序的结果: Student [id=1003, name=Wide] -------------------------------------------------- 按照姓名比较元素的大小: Student [id=1002, name=Bon] 按照姓名比较元素的大小: Student [id=1001, name=Mary] 按照姓名比较元素的大小: Student [id=1003, name=Wide]
代码:
package com.monkey1024; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Test { public static void main(String[] args) { Collection c = new ArrayList(); c.add(11); c.add(22); c.add(33); c.add(44); Iterator it = c.iterator(); while(it.hasNext()) { Object obj = it.next(); if(obj.equals(22)) { // c.remove(obj); //报错:并发修改异常 // 在迭代器中删除元素,要使用迭代器自己的 remove()方法进行删除!!! // 否则发生并发修改异常 it.remove(); } } System.err.println("打印删除元素22之后的集合c: "+c); } }
结果:
打印删除元素22之后的集合c: [11, 33, 44]
六、增强版 for循环 (for each)
1.语法格式:
for (元素类型 变量名 : 数组名或集合名){
循环体,使用变量名代表当前元素;
}
2.执行流程:
- 先声明元素类型的变量;
- 从数组或集合中取出元素赋值给变量
- 执行循环体
- 取出下一个元素赋值给变量
- 执行循环体
- ...............
- 元素取完为止
3.经验总结:
- 在以后的开发中,若只是遍历集合中的所有元素,则推荐使用for each循环结构;
- 在以后的开发中,若遍历集合的同时 还希望删除元素,则推荐使用迭代器结构。切记,调用的是迭代器自己的remove()方法,而不能是 集合的remove()方法。否则会产生并发修改异常。
集合的遍历方式:3种
01.toString()
02.迭代器
03.for each
对于 List 这种集合,还有get()这种方法可以遍历集合。
七、泛型机制
八、Collections类
java.util.Collections类是Java官方提供的一个工具类,该类提供了大量针对集合中元素操作的方法。如max()获取集合中最大值。