Java基础19-数据结构(容器)- Collection集合介绍
数据结构,容器:
用来装对象…,等各种管理对象的容器。
容器有相同的操作标准:
(1)增
(2)删
(3)改
(4)查
Java给这些集合抽取两大接口:
1、Collection:
规范单值集合的接口,obj --》单身party
2、Map
规范对值集合的接口,(key,value) -->情侣party
一、Collection
1、Collection:接口
(1)它是根接口
(2)它没有直接的实现类,有更具体的子接口:List和Set…
(3)有一些的元素是可以重复的(例如list),有些集合的元素是不能重复(例如set),有些集合的元素是有序的(例如list),有些集合的元素是无序的(例如set)
2、API
(1)添加
add(Object obj):添加一个元素
addAll(Collection c):添加多个元素
(2)获取有效元素的个数
int size()
实现
add(Object obj):添加一个元素
@SuppressWarnings("all")
@Test
public void test1(){
/*
* 我这里左边写Collection,目的是只关注Collection的方法。实现类里可能有更具体的方法
* 因为多态引用时,c编译期间只能访问Collection的方法
*/
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
System.out.println("获取有效元素的个数:" + c.size());
}
addAll(Collection c):添加多个元素
@SuppressWarnings("all")
@Test
public void test2(){
/*
* 我这里左边写Collection,目的是只关注Collection
* 因为多态引用时,c编译期间只能访问Collection的方法
*/
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add(1);
c.add(2);
c.add(3);
Collection c2 = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c2.add("张三");
c2.add("李四");
c2.add("王五");
c.addAll(c2);//把c2中的所有元素都添加到c集合中,c中一个6个元素
// c.add(c2);//c中一共4个元素,将c2作为一个元素放进去
System.out.println("获取有效元素的个数:" + c.size());
System.out.println(c);
}
结果:
c.addAll(c2);
c.add(c2);
(3)是否包含
contains(Object o) :判断o是否在当前的集合中
containsAll(Collection c) :判断c是否是当前集合的子集
@SuppressWarnings("all")
@Test
public void test3(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
System.out.println(c.contains("张三"));
System.out.println(c.contains("杨洪强"));
}
public void test4(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c2.add("张三");
c2.add("李四");
Collection c3 = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c3.add("张三");
c3.add("杨洪强");
System.out.println(c.containsAll(c2));//c2是c的子集
System.out.println(c.containsAll(c3));//c3不是c的子集
}
(4)boolean isEmpty() :判断当前集合是否为空
等价于 集合对象.size()==0
(5)remove(Object o):删除一个
removeAll(Collection c):删除多个 this = this - this ∩ c
clear():清空所有
@SuppressWarnings("all")
@Test
public void test5(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
c.remove("张三");//删除一个
System.out.println(c);//显示元素,说明ArrayList重写了toString
}
@SuppressWarnings("all")
@Test
public void test6(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c2.add("张三");
c2.add("李四");
c.removeAll(c2);
System.out.println(c);//说明ArrayList重写了toString
}
@SuppressWarnings("all")
@Test
public void test7(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c2.add("张三");
c2.add("杨洪强");
c.removeAll(c2);
System.out.println(c);//说明ArrayList重写了toString
}
(6)retainAll(Collection<?> c) :保留交集 this = this ∩ c
@SuppressWarnings("all")
@Test
public void test8(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c2.add("张三");
c2.add("杨洪强");
c.retainAll(c2);
System.out.println(c);
System.out.println(c2);
}
(7)Object[] toArray() :把集合中的元素用一个数组返回
@SuppressWarnings("all")
@Test
public void test9(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
Object[] all = c.toArray();
System.out.println(Arrays.toString(all));
}
没有修改方法,因为collection是更高层次的一个接口,若是想修改,要看它的子接口List和Set会有增加其他方法,在collection里面把所有单值集合的共同的方法抽象到collection这里来
collection系列集合的遍历三种方式
Collection系列的集合的遍历:挨个访问集合的元素
(1)Object[] toArray():先返回数组,然后遍历数组
(2)迭代器设计模式
每一个Collection系列的集合,内部都自带一个迭代器,类似于,每一趟公交车上都有自己的售票员
java.util.Iterator:接口
它是所有售票员的标准接口。
(1)判断是否还有下一个元素:hasNext();
(2)访问它的下一个元素:next()
(3)请下一个元素下车:remove()
@SuppressWarnings("all")
@Test
public void test1(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
//返回这个集合自带的迭代器对象,相当于你找到了售票员
//让售票员去挨个的访问元素
Iterator iterator = c.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
}
//一开始指针位于第一个元素之前,若是有元素,即hasNext(),就取得第一个元素,Object obj = iterator.next();,指针后移,接着判断,然后取值,以此类推
@SuppressWarnings("all")
@Test
public void test2(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
//返回这个集合自带的迭代器对象,相当于你找到了售票员
//让售票员去挨个的访问元素
Iterator iterator = c.iterator();
while(iterator.hasNext()){
String obj = (String) iterator.next();
//要姓“王”下车
if(obj.startsWith("王")){
iterator.remove();
}
}
System.out.println(c);
}
``
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200919194445848.png#pic_center)
java.util.Iterator:迭代器接口,这个接口的实现类在每一种集合类中,例如:ArrayList内部有一个内部类实现了Iterator接口
这里声明为内部类有两个原因:
(1)每一种集合的内部实现(物理结构不同),意味着对迭代器(售票员)的实现是不同的,每一种集合都要单独定制迭代器
(2)内部类可以直接访问外部类的私有的属性,成员,迭代器就可以直接访问集合的私有的元素。
**(3)foreach:增强for循环**
foreach循环可以用于遍历数组、Collection系列的集合等容器。
语法结构:
for(元素的类型 元素临时名称 : 数组和集合名){
}
不同于普通for循环。
for(int i=0; i<5; i++){
}
```java
@SuppressWarnings("all")
@Test
public void test3(){
Collection c = new ArrayList();//ArrayList是Collection下面的一个实现类而已
c.add("张三");
c.add("李四");
c.add("王五");
//Object:元素的数据类型
//obj:临时的元素名称
//c:要遍历的集合的名称
for (Object obj : c) {
System.out.println(obj);
}
}
@SuppressWarnings("all")
@Test
public void test4(){
String[] arr = {"hello","world","java"};
for (String string : arr) {
System.out.println(string);
}
}
什么样集合或容器类型可以使用foreach循环?
凡是实现了**java.lang.Iterable(可迭代)**接口(可迭代)的集合或容器都支持foreach循环
@Test
public void test5(){
//我自己写的动态数组
MyArrayList list = new MyArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
for (Object obj : list) {
System.out.println(obj);
}
}
自己写的动态数组(之前案例)不能使用foreach循环
若是想支持,实现Iterable接口
要实现接口的一个抽象方法iterator()
返回一个迭代器
collection可以用迭代器是因为继承了Iterable接口
foreach底层还是调用Iterator迭代器来遍历集合。
若是实现Iterator,要实现两个方法
remove在iterator中不是抽象方法,可重写可不重写
完整代码:
public class MyArrayList implements Iterable{
//为什么使用Object,因为只是说这个容器是用来装对象的,但是不知道用来装什么对象。
private Object[] data;
private int total;
public MyArrayList(){
data = new Object[5];
}
//添加一个元素
public void add(Object obj){
//检查是否需要扩容
checkCapacity();
data[total++] = obj;
}
private void checkCapacity() {
//如果data满了,就扩容为原来的2倍
if(total >= data.length){
data = Arrays.copyOf(data, data.length*2);
}
}
//返回实际元素的个数
public int size(){
return total;
}
//返回数组的实际容量
public int capacity(){
return data.length;
}
//获取[index]位置的元素
public Object get(int index){
//校验index的合理性范围
checkIndex(index);
return data[index];
}
private void checkIndex(int index) {
if(index<0 || index>=total){
throw new RuntimeException(index+"对应位置的元素不存在");
// throw new IndexOutOfBoundsException(index+"越界");
}
}
//替换[index]位置的元素
public void set(int index, Object value){
//校验index的合理性范围
checkIndex(index);
data[index] = value;
}
//在[index]位置插入一个元素value
public void insert(int index, Object value){
/*
* (1)考虑下标的合理性
* (2)总长度是否够
* (3)[index]以及后面的元素往后移动,把[index]位置腾出来
* (4)data[index]=value 放入新元素
* (5)total++ 有效元素的个数增加
*/
//(1)考虑下标的合理性:校验index的合理性范围
checkIndex(index);
//(2)总长度是否够:检查是否需要扩容
checkCapacity();
//(3)[index]以及后面的元素往后移动,把[index]位置腾出来
/*
* 假设total = 5, data.length= 10, index= 1
* 有效元素的下标[0,4]
* 移动:[1]->[2],[2]->[3],[3]->[4],[4]->[5]
* 移动元素的个数:total-index
*/
System.arraycopy(data, index, data, index+1, total-index);
//(4)data[index]=value 放入新元素
data[index] = value;
//(5)total++ 有效元素的个数增加
total++;
}
//返回所有实际存储的元素
public Object[] getAll(){
//返回total个
return Arrays.copyOf(data, total);
}
//删除[index]位置的元素
public void remove(int index){
/*
* (1)校验index的合理性范围
* (2)移动元素,把[index+1]以及后面的元素往前移动
* (3)把data[total-1]=null 让垃圾回收器尽快回收
* (4)总元素个数减少 total--
*/
//(1)考虑下标的合理性:校验index的合理性范围
checkIndex(index);
//(2)移动元素,把[index+1]以及后面的元素往前移动
/*
* 假设total=8, data.length=10, index = 3
* 有效元素的范围[0,7]
* 移动:[4]->[3],[5]->[4],[6]->[5],[7]->[6]
* 移动了4个:total-index-1
*/
System.arraycopy(data, index+1, data, index, total-index-1);
//(3)把data[total-1]=null 让垃圾回收器尽快回收
data[total-1] = null;
// (4)总元素个数减少 total--
total--;
}
//查询某个元素的下标
/* public int indexOf(Object obj){
for (int i = 0; i < total; i++) {
//这两种写法都有风险
if(obj.equals(data[i])){
//if(data[i].equals(obj)){
return i;//找到,返回第一个找到的
}
}
return -1;//没找到返回-1
}*/
//查询某个元素的下标
public int indexOf(Object obj){
if(obj == null){
for (int i = 0; i < total; i++) {
if(data[i] == null){//等价于 if(data[i] == obj)
return i;
}
}
}else{
for (int i = 0; i < data.length; i++) {
if(obj.equals(data[i])){
return i;
}
}
}
return -1;
}
//删除数组中的某个元素
//如果有重复的,只删除第一个
public void remove(Object obj){
/*
* (1)先查询obj的[index]
* (2)如果存在,就调用remove(index)删除就可以
*/
//(1)先查询obj的[index]
int index = indexOf(obj);
if(index != -1){
remove(index);
}
//不存在,可以什么也不做
//不存在,也可以抛异常
//throw new RuntimeException(obj + "不存在");
}
public void set(Object old, Object value){
/*
* (1)查询old的[index]
* (2)如果存在,就调用set(index, value)
*/
// (1)查询old的[index]
int index = indexOf(old);
if(index!=-1){
set(index, value);
}
//不存在,可以什么也不做
//不存在,也可以抛异常
//throw new RuntimeException(old + "不存在");
}
@Override
public Iterator iterator() {
return new MyItr();
}
private class MyItr implements Iterator{
private int cursor;//游标
@Override
public boolean hasNext() {
System.out.println("还有下一个");
return cursor!=total;
}
@Override
public Object next() {
System.out.println("拿到下一个");
return data[cursor++];
}
}
}