目录
一、集合的概述:
1、集合与数组的不同点:
2、集合的特点:
二、Collection接口:
1、Collection中的各种功能:
二、集合的遍历:
1、使用获取功能实现遍历:
三、 List接口(继承自Collection接口)
1、List接口的特点:
2、List相关集合拥有的功能:
引例:利用数组存储三个学生信息,遍历数组获取每个学生信息。
该例的实现较为简单,但是当数组中突然需要加入一个新同学的信息时,则需要重新创建一个长度为四的数组重新实现,若是数据过多则需要不断的创建新的数组去存放,这样的操作非常繁琐,于是Java可以根据元素的不同,元素的特点和存储的方式不同提供一个集合继承机制供使用。
1、数组的长度是不可变的,集合的长度是可变的
2、数组可以存放同一种基本数据类型或引用数据类型的元素,而集合只能存放引用数据类型的元素,但集合可以存放不同引用数据类型的元素。(实际开发中一个集合只存放一种引用数据类型元素)
集合只用于存储对象;集合长度是可变的;集合可以存放不同类型的对象。
集合不是单一的,针对不同的需求,使用Java提供的不同的集合。这些不同的集合应该有某种共性,根据他们之间的共性不断向上提取,最终形成一个继承机制。
集合的继承体系(机制)
简略图:
是集合的顶层接口,它存在由他展开而来的继承体系
由于Collection是一个接口,所以无法被实例化,我们需要找它一个子类来进行接口多态的方式实例化,暂时使用ArrayList来举例
1、添加功能:
boolean add(Object e) 确保此集合包含指定的元素(可选操作)
boolean addAll(Collection c) 将指定集合的所有元素添加到此集合(可选操作)
2、删除功能:
boolean remove(Object o) 从该集合中删除指定元素的单个实例(如果存在)
boolean removeAll(Collection c) 删除指定集合中包含的所有此集合的元素
void clear()从此集合中删除所有元素
3、获取功能:
Iterator iterator() 返回此集合中的元素的迭代器
4、判断功能:
boolean contains(Object o) 如果此集合包含指定元素,返回true
boolean containsAll(Collection c) 如果此集合包含指定集合中的所有元素,则返回true
boolean isEmpty() 如果此集合不包含元素,则返回true
5、获取长度方法:
int size() 返回此集合中的元素数
6、求交集功能:
boolean retainAll(Collection c) 仅保留此集合中包含在指定集合中的元素
7、将集合转为数组:
Object[] toArray() 返回一个包含此集合中的所有元素的数组
具体部分实现:
import java.util.ArrayList; import java.util.Collection; public class Test1 { public static void main(String[] args) { Collection c=new ArrayList(); // Collection的添加功能: c.add("hello"); c.add("world"); c.add("java"); c.add("bigdata"); System.out.println("添加后的集合:"+c); // Collection的删除功能remove: c.remove("world"); System.out.println("删除后的集合:"+c); // Collection的判断功能: System.out.println("判断是否含有world:"+c.contains("world")); // 判断是否为空: System.out.println("判断是否为空:"+c.isEmpty()); // 获取长度: System.out.println("集合的长度为"+c.size()); // Collection的删除功能clear: c.clear(); System.out.println("清除后的集合:"+c); } }
输出结果:
对于使用在两个集合之间的功能实现如下:
import java.util.ArrayList; import java.util.Collection; /* boolean addAll(Collection c) 将指定集合中的所有元素添加到此集合(可选操作) boolean removeAll(Collection c) 删除指定集合中包含的所有此集合的元素(可选操作)。 boolean containsAll(Collection c) 如果此集合包含指定 集合中的所有元素,则返回true。 boolean retainAll(Collection c) 仅保留此集合中包含在指定集合中的元素(可选操作)。 */ public class Test2 { public static void main(String[] args) { Collection c1 = new ArrayList(); c1.add("hello"); c1.add("world"); c1.add("java"); c1.add("bigdata"); System.out.println(c1); Collection c2=new ArrayList(); c2.add("hello"); c2.add("world"); c2.add("superman"); System.out.println(c2); System.out.println("=============================================="); System.out.println("将c2添加到c1:" ); c1.addAll(c2); System.out.println(c1); System.out.println(c2); System.out.println("=============================================="); System.out.println("判断c1是否包含c2:"); boolean b = c1.containsAll(c2); System.out.println(b); System.out.println("=============================================="); System.out.println("c1仅保留c2中的元素(取交集):"); // 取交集功能 c1.retainAll(c2); System.out.println(c1); System.out.println(c2); // c1对c2交集,结果保存在c1中,c2不变,且c1删除与c2不是共同的元素 System.out.println("=============================================="); System.out.println("将c1中删除包含c2中的元素:"); c1.removeAll(c2); System.out.println(c1); System.out.println(c2); } }
输出结果:
目的就是将集合中的元素依次提取出来
使用到功能7:将集合转为数组 : toArray()方法
具体实现为将集合转为数组,再对数组进行遍历即可:
import java.util.ArrayList; import java.util.Collection; public class Test3 { public static void main(String[] args) { Collection c=new ArrayList(); c.add("hello"); c.add("world"); c.add("java"); c.add("bigdata"); // 使用toArray()将集合转为数组 Object[] objects = c.toArray(); // 对数组进行遍历 for(int i=0;i< objects.length;i++){ System.out.println(objects[i]);//Object o = objects[i](String类型的) /* 这里使用Object类型接收,但是实际上是String类型 这就形成了一个多态 */ } } }
输出结果:
若是想要获取每一个字符串元素的长度,直接加入一行语句:
System.out.println(objects[i].length());
会报错:因为这里使用的是多态,编译看左,运行看右,Object类中没有length()方法。
所以需要向下转型,具体代码:
import java.util.ArrayList; import java.util.Collection; public class Test3 { public static void main(String[] args) { Collection c=new ArrayList(); c.add("hello"); c.add("world"); c.add("java"); c.add("bigdata"); // 使用toArray()将集合转为数组 Object[] objects = c.toArray(); // 这里由于不清楚该集合转为数组后是什么数据类型,因此使用Object[]类型接收 // 对数组进行遍历 for(int i=0;i< objects.length;i++){ // System.out.println(objects[i]); // 获取每个字符串元素的长度: // 向下转型: String s=(String)objects[i]; System.out.println(s+"---"+s.length()); } } }
输出结果:
学生实例:
import java.util.ArrayList; import java.util.Collection; /* 需求:向集合中添加3个学生对象,并遍历学生学生信息 */ public class Test3a { public static void main(String[] args) { // 创建集合对象 Collection c=new ArrayList(); // 创建学生对象 Student s1=new Student("马超",18); Student s2=new Student("关羽",17); Student s3=new Student("张飞",16); // 将学生对象添加到集合中 c.add(s1); c.add(s2); c.add(s3); // 将集合转换为数组 Object[] objects = c.toArray(); // 遍历数组 for (int i=0;i<objects.length;i++){ // 向下转型 Student s=(Student)objects[i]; System.out.println(s.getName()+"---"+s.getAge()); } } }
输出结果:
Iterator iterator() 返回此集合中的元素的迭代器。 它是Collection集合遍历的专有方式
迭代器是一个接口
两个主要方法:
boolean hasNext() 判断迭代器中是否还有元素
Object next() 返回迭代器中的元素
使用迭代器实现遍历:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Test4 { public static void main(String[] args) { Collection c=new ArrayList(); c.add("hello"); c.add("world"); c.add("java"); c.add("bigdata"); // 获取迭代器对象 Iterator iterator = c.iterator();//Iterator iterator = new Itr(); // 该接口的具体实现是类Itr() // System.out.println(iterator);//java.util.ArrayList$Itr@4554617c // 使用next()获取下一个元素 Object next = iterator.next(); System.out.println(next);//获取hello System.out.println(iterator.next());//获取world System.out.println(iterator.next());//获取java System.out.println(iterator.next());//获取bigdata // System.out.println(iterator.next());//NoSuchElementException /* 当无意中多使用一次next方法时,报错 原因是该遍历已经执行到最后一个元素了,不应该再指向下一个元素,是多余的 */ } }
输出结果:
上述代码中出现的问题的解决办法:在执行next方法前加入判断:hasnext()
以下为改进代码:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Test4 { public static void main(String[] args) { Collection c=new ArrayList(); c.add("hello"); c.add("world"); c.add("java"); c.add("bigdata"); // 获取迭代器对象 Iterator iterator = c.iterator();//Iterator iterator = new Itr(); // 该接口的具体实现是类Itr() // System.out.println(iterator);//java.util.ArrayList$Itr@4554617c // 解决办法:在执行next方法前加入判断:hasnext() if(iterator.hasNext()){ System.out.println(iterator.next()); } if(iterator.hasNext()){ System.out.println(iterator.next()); } if(iterator.hasNext()){ System.out.println(iterator.next()); } if(iterator.hasNext()){ System.out.println(iterator.next()); } if(iterator.hasNext()){ System.out.println(iterator.next()); } } }
输出结果同上面相同,但这里加入了判断之后,使用了五次if判断和next方法,但不影响最终结果。若是添加到集合中的数据量过于庞大,使用if语句的次数便不明确,这里使用while循环进一步进行改进。
最终改进代码为:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Test4 { public static void main(String[] args) { Collection c=new ArrayList(); c.add("hello"); c.add("world"); c.add("java"); c.add("bigdata"); // 获取迭代器对象 Iterator iterator = c.iterator();//Iterator iterator = new Itr(); // 该接口的具体实现是类Itr() // System.out.println(iterator);//java.util.ArrayList$Itr@4554617c // 使用while循环改进: while(iterator.hasNext()){ Object next = iterator.next(); String s=(String)next; System.out.println(s+"---"+s.length()); } } }
最终输出结果:
1、 能否将while循环改为for循环?
可以,但是一般情况下不推荐使用普通for循环遍历
2、java为什么要将iterator定义为一个接口而不是一个类?
将来需要根据不同的数据创建不同的集合去存储,每个集合都有自身特点,很有可能每一个集合遍历的顺序和方式都不一样 所以将来取值的时候,使用的方式也不一定是一样的,所以迭代器不应该直接实现如何遍历,而是提供一个接口 将来特有的集合类去实现这个接口中的取值方法,来实现自身的取值特点。
相关案例:
存储自定义对象并遍历:
首先定义一个Student类:
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } 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; } }
其次相关代码如下:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /* 存储自定义对象并遍历 */ public class CollectionTest2 { public static void main(String[] args) { //1、创建集合对象 Collection c = new ArrayList(); //2、创建学生对象 Student s1 = new Student("张飞", 17); Student s2 = new Student("关羽", 18); Student s3 = new Student("赵云", 19); Student s4 = new Student("黄忠", 20); Student s5 = new Student("马超", 21); //3、将学生对象添加到集合中 c.add(s1); c.add(s2); c.add(s3); c.add(s4); c.add(s5); //4、遍历集合 //获取迭代器对象 Iterator iterator = c.iterator(); //遍历迭代器获取元素 while (iterator.hasNext()) { Student s = (Student) iterator.next(); System.out.println(s.getName() + "---" + s.getAge()); } } }
输出结果:
1、List集合中的元素是有序的(存储和取出顺序相同)存储1,2,3,4,5,输出1,2,3,4,5
2、List集合包含了索引的概念
3、List集合中的元素是可以重复的
相关案例:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ListTest1 { public static void main(String[] args) { // 创建List集合对象 List list=new ArrayList(); // 添加元素 list.add("hello"); list.add("world"); list.add("java"); list.add("bigdata"); // 遍历 Iterator iterator=list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } } }
输出结果:
因为List集合拥有下标索引的概念,故根据这个概念衍生出特有方法:
1、添加功能:
void add(int index, Object element)
将指定的元素插入此列表中的指定位置(可选操作)。
2、删除功能:
Object remove(int index) 删除该列表中指定位置的元素(可选操作)。
3、获取功能:
Object get(int index) 返回此列表中指定位置的元素。
4、修改功能:
Object set(int index, Object element) 用指定的元素(可选操作)替换此列表中指定位置的元素。
5、List集合特有的迭代器
ListIterator listIterator() 返回列表中的列表迭代器(按适当的顺序)。
功能1到4的实现:
import java.util.ArrayList; import java.util.List; public class ListTest2 { public static void main(String[] args) { List list=new ArrayList(); list.add("hello"); list.add("world"); list.add("java"); list.add("bigdata"); System.out.println(list); // 添加功能: list.add(0,"hadoop"); System.out.println(list); list.add(2,"hive"); System.out.println(list); list.add(6,"spark"); System.out.println(list); // list.add(10,"flink"); // System.out.println(list);//IndexOutOfBoundsException // 这里会有索引越界异常,因此得出添加索引范围为: // 0<=index<=size(),一旦超出该范围则会异常 System.out.println("============================================"); System.out.println("删除前:\r\n"+list); // 删除功能: System.out.println(list.remove(3)); System.out.println("删除后:\r\n"+list); System.out.println("============================================"); // 获取功能: System.out.println("获取元素:"+list.get(4)); System.out.println(list); System.out.println("============================================"); // 修改功能: Object o = list.set(4, "Superman"); System.out.println("被修改的元素:"+o); System.out.println(list); } }
输出结果:
List特有的迭代器:
ListIterator listIterator() 返回列表中的列表迭代器(按适当的顺序)。
public interface ListIterator extends Iterator
由于继承自Iterator接口,故内部一定具有hasNext()和next()方法
其中还含有的方法:
Object previous() 返回列表的上一个元素,并向后移动光标位置
可以反复调用此方法以向后方遍历列表,或者与调用next()进行混合来回。
1、该方法是获取集合中前一个元素
2、该方法获取元素的指针与next()获取元素的指针是同一个
注意:要想倒着遍历,就必须先正着遍历,先将指针移动到末尾。在开发中不常用,但是在面试中可能会问到。
boolean hasPrevious() 返回 true如果遍历反向列表,列表迭代器有多个元素。 判断上一个位置是否有元素,如果有元素返回true,如果没有元素,返回false。
代码举例:
import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class ListDemo3 { public static void main(String[] args) { //1、创建List集合对象 List list = new ArrayList(); //2、添加元素到集合中 list.add("hello"); list.add("world"); list.add("java"); list.add("bigdata"); //3、遍历 // Object previous = listIterator.previous(); // System.out.println(previous); //NoSuchElementException // 若是直接使用previous会报错,因为此时光标是在第一个位置,没有上一个元素 ListIterator listIterator = list.listIterator(); while (listIterator.hasNext()) { String s = (String) listIterator.next(); System.out.println(s + ",字符串的长度为:" + s.length()); } System.out.println("==============使用previous================"); while (listIterator.hasPrevious()){ Object previous = listIterator.previous(); System.out.println(previous); } } }
输出结果:
List集合特有的遍历方式:size()与get()方法结合使用:
具体实现:三种方法一同实现:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ListTest3 { public static void main(String[] args) { List list=new ArrayList(); Student s1=new Student("刘备",51); Student s2=new Student("曹操",45); Student s3=new Student("孙权",40); list.add(s1); list.add(s2); list.add(s3); // 遍历 // a:转成数组遍历 Object[] obj = list.toArray(); for(int i=0;i< obj.length;i++){ Student s=(Student)obj[i]; System.out.println(s.getName()+"---"+s.getAge()); } System.out.println("==============================================="); // b:迭代器遍历: Iterator iterator = list.iterator(); while(iterator.hasNext()){ Student s=(Student)iterator.next(); System.out.println(s.getName()+"---"+s.getAge()); } System.out.println("==============================================="); // c:size()与get()方法结合使用 for(int i=0;i< list.size();i++){ Student s=(Student)list.get(i); System.out.println(s.getName()+"---"+s.getAge()); } } }
输出结果:
现在有一个需求:有一个集合,集合中存储着一些字符串类型的元素,我想判断一下里面有没有"bigdata"这个字符串 如果有,我们就添加一个"yes"。
按照我们上述所学知识,使用Iterator迭代器遍历即可:
但实际上当我们使用迭代器遍历时,会报错:
ConcurrentModificationException: 并发修改异常 当不允许这样的修改的时候,java就检测到该对象出现了并发修改异常。
原因:
迭代器是依赖于集合而存在的,在遍历迭代器中的元素的时候,当我们判断成功后,往集合中添加一个元素,但是这时候,迭代器并不知道已经添加了元素,所以迭代器依然不变,但此时迭代器的元素和集合就不一致了,所以就报错了。
简单描述:在迭代器遍历的时候,不能通过集合去修改元素
解决办法:迭代器遍历,迭代器修改;集合遍历,集合修改
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; public class ListDemo6 { public static void main(String[] args) { //1、创建一个集合对象 List list = new ArrayList(); //2、向集合中添加元素 list.add("hello"); list.add("world"); list.add("java"); list.add("bigdata"); list.add("hive"); //遍历 // Iterator iterator = list.iterator(); // while (iterator.hasNext()){ // Object next = iterator.next(); // String s = (String) next; // if("bigdata".equals(s)){ // list.add("yes"); // } // } // 以下为两种解决办法: //迭代器遍历,迭代器修改 // ListIterator listIterator = list.listIterator(); // while (listIterator.hasNext()){ // Object next = listIterator.next(); // String s = (String) next; // if("bigdata".equals(s)){ // listIterator.add("yes"); //在bigdata后面添加,因为此时指针正好指到这里 // } // } //集合遍历,集合修改 for(int i=0;i<list.size();i++){ String s = (String) list.get(i); if("bigdata".equals(s)){ list.add("yes"); //在集合末尾添加 } } System.out.println(list); } }
输出结果:两种输出结果:
迭代器遍历输出结果:yes加在当前光标所处位置
集合遍历输出结果:yes加在末尾