Java笔记day21
一、先写一个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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
二、集合
1、数组的弊端
需求:利用数组存储3个学生信息,遍历数组获取每一个学生的信息
查看代码
public class ObjectArrayDemo1 {
public static void main(String[] args) {
Student[] arr = new Student[3];
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("---------------------------------");
//创建3个学生对象
Student xiaowang = new Student("王力宏", 40);
Student dengdeng = new Student("邓超", 45);
Student huazai = new Student("刘德华", 60);
//将创建好的3个学生对象,放到数组
//给对象数组赋值,使用循环是有问题的,因为对象的名字没有规律,没办法
//将每次循环依次赋值不一样的对象
//所以给对象数组赋值,只能一个一个赋值
arr[0] = xiaowang;
arr[1] = dengdeng;
arr[2] = huazai;
//遍历数组获取每一个学生对象
for (int i = 0; i < arr.length; i++) {
//System.out.println(arr[i]);
Student student = arr[i];
System.out.println(student.getName() + "--" + student.getAge());
}
}
}
赋值即过程解读
1)、Student[] arr = new Student[3];
程序执行到这里,方法区里的class文件区有Student.class和ObjectArrayDemo1.class
在栈里先建立一个main()主方法区,放入Student[] arr
在堆里新建new Student[3];数组,此时未赋值,输出arr[i]的时候是三个null
此时假设new Student[3]地址值是0x01,把0x01的值赋值给Student[] arr
2)、Student xiaowang = new Student("王力宏", 40);...
在栈里新建Student xiaowang,而在堆里的区域新建new Student
赋值之前new Student里面是两个null,赋值之后是("王力宏", 40),假设这里的地址值是0x001
把0x001赋给xiaowang,同样的道理在栈和堆里建立后面的两个对象
3)、arr[0] = xiaowang;...
依次给arr[]里的三个null赋上xiaownang...三个对象的地址值
弊端
假设在存储完所有的学生对象的后,来了一个新同学A,也要放到该数组中,直接放进去的话,
由于数组长度已经定下了,只能新建一个数组来存储,新建的数组长度是原来数组长度+1,然后再挨个存放数组
如果这时候,有一个学生B提前毕业,数组中的元素就少了一个,那么现在数组中就有一个空的位置,
而这个空的位置依旧是占用内存的,也不太好,所以又要创建一个新的数组,长度-1,然后挨个存放。
这个例子,无论是从增加还是删除,都非常麻烦,其实原则上我们修改是根据原先的东西基础上进行修改的。
这时候我们想到之前还学过一个容器,叫做StringBuffer,它就可以根据元素的多少来改变长度。但是,
StringBuffer里面存储始终是一个一个的字符,而我们现在需要存放学生对象,所以用StringBuffer也不合适。
2、集合的引入
1)、集合概述
在上一个例子当中,就充分体现了数组和String Buffer的弊端,那么有没有一个新的东西让我们来解决这个问题呢
Java替我们考虑到了这一点,根据存储的元素不同,元素的特点不同以及存储方式不同,提供了一个集合继承体系给我们,
简单来说,就是集合
集合和数组的不同点:
1、数组的长度是不可变的,集合是可以改变
2、数组可以存放同一种基本数据类型或者引用数据类型
而集合只能存放引用数据类型,并且集合中可以存放不同的引用数据类型
(我们虽然说了集合可以存放不同的引用数据类型,确实也可以这么做,但是,开发中
一个集合存放一种引用数据类型。)
集合可以存放各种各样的数据,每种数据的长度,大小以及自身的特点都不一样。
所以,java提供的集合也应该不能够是单一的,我们要针对不同的需求,java提供不同的集合类。
这么多不同的集合,它们的底层数据结构也是不同的,不同并不重要,我们只要知道集合是可以用来
存放东西的,不光可以存放,而且可以去使用这些东西,比如:查找获取,判断等等。
既然可以上面的操作,这些不同的集合类应该有某种共性的内容,所以我们根据集合的共性内容不断地
向上提取,最终整体形成一个继承体系。
2)、集合以及集合的功能
Collection:
是集合中的顶层接口,它存在由它扩展开来的继承体系,为什么要分出很多不同的集合?
根据元素是否唯一,是否有序来区分这么多集合(后面的课程中会一一介绍)(List也是接口,ArrayList那一层是类)
集合学习体系
图
Collection:
1、添加功能
boolean add(Object obj) 确保此集合包含指定的元素(可选操作)。
boolean addAll(Collection c)
将指定集合中的所有元素添加到此集合(可选操作)。
2、删除功能
boolean remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
boolean removeAll(Collection<?> c)
删除指定集合中包含的所有此集合的元素(可选操作)。
void clear()
从此集合中删除所有元素(可选操作)。
3、获取功能
Iterator<E> 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 CollectionDemo1 {
public static void main(String[] args) {
//通过子类的形式创建对象,这叫接口多态
Collection c = new ArrayList();
//boolean add(Object obj) 确保此集合包含指定的元素(可选操作)。
System.out.println(c.add("hello")); //只能判定插入成功或者失败
System.out.println(c.add("hello")); //允许有重复的元素
c.add(20); //自动装箱转成了Interger类
c.add(12.34);
//void clear()
//从此集合中删除所有元素(可选操作)。
//c.clear();
//boolean remove(Object o)
//从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
//只移除一个符合条件的
System.out.println("从该集合中删除指定元素" + c.remove("hello")); //clear清除之后不含有Hello,false
//boolean contains(Object o)
//如果此集合包含指定的元素,则返回 true 。
System.out.println(c.contains("hello")); //上面只删除了一个,true
//boolean isEmpty()
//如果此集合不包含元素,则返回 true 。
System.out.println(c.isEmpty()); //false
//获取长度功能int size()
// 返回此集合中的元素数。
System.out.println(c.size()); //3
//String toString()
//返回此集合的字符串表示形式。 AbstractCollection类中的toString()方法
/**
* java.lang.Object
* java.util.AbstractCollection<E> //toString是在这里重写的
* java.util.AbstractList<E>
* java.util.ArrayList<E>
*/
System.out.println("集合c: " + c); //重写了toString方法,而且输出的时候还有中括号和逗号
}
}
3、集合中接口成员方法
boolean addAll(Collection c)
boolean removeAll(Collection c)
boolean containsAll(Collection c)
boolean retainAll(Collection c)
查看代码
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo2 {
public static void main(String[] args) {
//创建一个集合对象
Collection c1 = new ArrayList();
//向集合中添加元素
c1.add("hello");
c1.add("world");
c1.add("java");
c1.add("hadoop");
c1.add("hive");
//c1.add("spark");
//定义另一个集合
Collection c2 = new ArrayList();
c2.add("hello");
c2.add("world");
c2.add("hive");
c2.add("spark");
System.out.println("c1: "+c1);
System.out.println("c2: "+c2);
System.out.println("===============================");
//boolean addAll(Collection c)
//System.out.println("将c2添加到从c1中:");
//System.out.println(c1.addAll(c2)); //add是把c2当作一个元素整体添加到c1,addAll是把c2的每一个元素添加到c1中
//System.out.println("c1: "+c1);
//System.out.println("c2: "+c2);
//System.out.println("===============================");
//boolean removeAll(Collection c) 删除指定集合中包含的所有此集合的元素(可选操作)。
// 此调用返回后,此集合将不包含与指定集合相同的元素。
//System.out.println(c1.removeAll(c2));
//System.out.println("c1: "+c1);
//System.out.println("c2: "+c2);
//System.out.println("===============================");
//boolean containsAll(Collection c)如果此集合包含指定 集合中的所有元素,则返回true。
//System.out.println(c1.containsAll(c2));
System.out.println("===============================");
//boolean retainAll(Collection c)
//仅保留此集合中包含在指定集合中的元素(可选操作)。
// 换句话说,从该集合中删除所有不包含在指定集合中的元素。
//假设有两个集合 c1,c2
//c1对c2做交集,最终的结果保存在c1中,c2不变
//并且c1中删除与c2不是共同的其他元素
//System.out.println(c1.retainAll(c2));
//System.out.println("c1: "+c1);
//System.out.println("c2: "+c2);
}
}
三、迭代器
1、不使用迭代器的遍历
集合的遍历:目的就是将集合中的元素依次取出来
Object[] toArray()
把集合转成数组,可以实现集合的遍历
例一:
查看代码
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo3 {
public static void main(String[] args) {
//创建集合对象
Collection c1 = new ArrayList();
//添加元素
c1.add("hello");
c1.add("world");
c1.add("java");
c1.add("20");
//c1.add(10);
//将集合转换成数组
Object[] array = c1.toArray();
//遍历数组获取数组中每一个元素
for (int i = 0; i < array.length; i++) {
//System.out.println(array[i]);
//因为获取到的元素类型是Object类型
//元素是Object,但是数组本身有.length方法
//所以没有length()方法
//要想调用字符串中的方法,就需要进行向下转型
//System.out.println(array[i].length());
String s = (String) array[i];
//如果集合中有数字,那么此处转型就不能就会报错,Interger类型不能转型成Strig类型
//所以集合中得到与元素最好都是同一种类型
System.out.println(s + ",长度为:" + s.length());
}
}
}
例二:
需求:向集合中添加3个学生对象,并遍历学生信息
查看代码
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo4 {
public static void main(String[] args) {
//创建集合
Collection c1 = new ArrayList();
//创建3个学生对象
Student s1 = new Student("李玉伟", 18);
Student s2 = new Student("刘生发", 17);
Student s3 = new Student("朱佳乐", 16);
//将学生对象添加到集合中
c1.add(s1);
c1.add(s2);
c1.add(s3);
//将学生对象集合转成数组
Object[] array = c1.toArray();
//遍历数组
for (int i = 0; i < array.length; i++) {
//向下转型,转成元素的类型
Student s = (Student) array[i];
System.out.println(s.getName() + "--" + s.getAge());
}
}
}
2、使用迭代器遍历
Iterator iterator()
迭代器,集合的专用遍历方式
boolean hasNext()
如果迭代具有更多元素,则返回 true 。
Object next()
返回迭代中的下一个元素。
查看代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo5 {
public static void main(String[] args) {
//创建集合对象
Collection c1 = new ArrayList();
//向集合中添加元素
c1.add("hello");
c1.add("world");
c1.add("java");
c1.add("hadoop");
//获取c1的迭代器对象
Iterator iterator = c1.iterator();
//System.out.println(iterator); 输出java.util.ArrayList$Itr@4554617c
//第一种迭代
Object next() 返回迭代中的下一个元素。
Object obj = iterator.next();
System.out.println(obj);
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
//NoSuchElementException,迭代所有元素之后如果继续迭代会出现该异常
System.out.println(iterator.next());
//第二种迭代
/*我们通过观察发现,最后一个System.out.println(iterator.next());
是多余的,因为此刻已经将迭代器中的元素遍历完了,不应该写,
我们实际应该在获取之前判断一下下一个位置上是否有元素,如果有就next()获取
如果没有就不获取
boolean hasNext() 如果迭代具有更多元素,则返回 true
*/
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(iterator.hasNext()){
System.out.println(iterator.next());
}
if(iterator.hasNext()){
System.out.println(iterator.next());
}
//第三种迭代
/*通过加入了判断我们发现,虽然代码不报错,也可以将结果元素正确的打印
但是由于不知道最后一个元素什么时候遍历,所以需要的迭代器个数也是未知的
此时,可以用循环改进
*/
//不知道什么时候结束首先想到while循环
while (iterator.hasNext()) {
Object next = iterator.next();
//向下转型
String s = (String) next;
System.out.println(s + ",长度为:" + s.length());
}
//for循环迭代,但是不推荐推荐使用while循环
Iterator iterator = c1.iterator();
for(;iterator.hasNext();){
Object next = iterator.next();
//向下转型
Student s = (Student) next;
System.out.println(s.getName() + "--" + s.getAge());
}
}
}
3、为什么将Iterator一个接口呢?而不是一个类呢?
将来你需要根据不同的数据创建不同的集合进行存储,每个集合都有自身独有特点,很有可能每一个
集合遍历的顺序特点不一样,所以取值的方式也很有可能不一样,所以不应该直接实现,而是通过一个接口
将来特有的集合要去使用迭代器时候,就实现一下这个接口,添加自己特有的遍历元素逻辑代码。
四、集合例题
/*
需求:将5个学生对象添加到集合中并遍历
Collection集合编写代码,完整代码1.0版本
*/
查看代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo7 {
public static void main(String[] args) {
//创建学生集合对象
Collection c1 = new ArrayList();
//创建学生对象
Student s1 = new Student("罗翔", 16);
Student s2 = new Student("周某人", 17);
Student s3 = new Student("徐磊", 18);
Student s4 = new Student("张牧野", 20);
Student s5 = new Student("张三", 21);
//将学生对象添加到集合中
c1.add(s1);
c1.add(s2);
c1.add(s3);
c1.add(s4);
c1.add(s5);
//获取集合中的迭代器对象
Iterator iterator = c1.iterator();
//遍历迭代器元素
while (iterator.hasNext()) {
Object next = iterator.next();
//向下转型
Student s = (Student) next;
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
五、集合Collection的子接口List
1、有序集合(有序指的是存储和取出的顺序一致,不是指排序),允许重复的元素
举例
查看代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo1 {
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()) {
Object next = iterator.next();
//向下转型
String s = (String) next;
System.out.println(s + ",长度为:" + s.length());
}
}
}
2、List相关集合特有的功能
因为List集合拥有下标索引,所以根据这个下标索引衍生出特有方法
添加功能:
void add(int index,Object element)
将指定的元素插入此列表中的指定位置(可选操作)。
删除功能:
Object remove(int index)
删除该列表中指定位置的元素(可选操作)。
获取功能:
Object get(int index)
返回此列表中指定位置的元素。
修改功能:
Object set(int index,Object element)
用指定的元素(可选操作)替换此列表中指定位置的元素。
List特有迭代器:
ListIterator<E> listIterator()
返回列表中的列表迭代器(按适当的顺序)。
举例
查看代码
import org.omg.CORBA.StringHolder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args) {
//创建List集合对象
List list = new ArrayList();
//添加元素到集合中
list.add("hello");
list.add("world");
list.add("java");
list.add("hadoop");
System.out.println(list);
System.out.println("===============================");
//void add(int index,Object element)
//将指定的元素插入此列表中的指定位置(可选操作)
//如果指数超出范围( index < 0 || index > size() ),会抛出异常
//索引是多少,就把那个位置上的元素往后推一位,然后添加
list.add(0, "hive");
System.out.println(list);
list.add(5, "spark");
System.out.println(list);
//注意该方法的索引取值范围
//IndexOutOfBoundsException: Index: 7, Size: 6
//list.add(7, "flink");
//System.out.println(list);
System.out.println("********************************");
//Object remove(int index)
//删除该列表中指定位置的元素(可选操作)。
//返回被删除的元素,此处为hive,类型为Object类型
Object obj = list.remove(0);
System.out.println(obj);
System.out.println(list);
System.out.println("******************************");
//Object get(int index)
//返回此列表中指定位置的元素。
//不改变原集合中的元素
Object obj2 = list.get(3);
System.out.println(obj2);
System.out.println(list);
System.out.println("******************************");
//Object set(int index,Object element)
//用指定的元素(可选操作)替换此列表中指定位置的元素。
//返回的是指定位置被替换的元素
Object obj3 = list.set(3, "flink");
System.out.println(obj3);
System.out.println(list);
System.out.println("******************************");
//获取迭代器对象
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String s = (String) iterator.next();
System.out.println(s + ",长度为:" + s.length());
}
}
}
3、List集合特有的迭代器和遍历方法
ListIterator extends Iterator
由于继承自Iterator接口,所以内部一定也有hasNext()方法和next()方法
Object previous()
返回列表中的上一个元素,并向后移动光标位置。
可以反复调用此方法以向后方遍历列表,或者与调用next()进行混合来回。
(请注意,对next和previous交替调用将next返回相同的元素。)
1、该方法是倒着遍历迭代器
2、该方法的获取元素指针与next()获取元素指针是同一个,调用next()是下一个,调用previous()是上一个
boolean hasPrevious()返回true如果遍历反向列表,列表迭代器有多个元素,判断上一个元素有没有值
(换句话说,如果previous()将返回一个元素而不是抛出异常,则返回true )。
注意:
要想倒着遍历,必须先正着遍历一遍。在开发中很少使用,面试中经常遇到。
查看代码
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorDemo1 {
public static void main(String[] args) {
//创建集合对象
List list = new ArrayList();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
list.add("hadoop");
ListIterator listIterator = list.listIterator();
// while (listIterator.hasPrevious()) {
// Object previous = listIterator.previous();
// String s = (String) previous;
// System.out.println(s + ",长度为:" + s.length());
// }
//迭代器指向第一个元素,第一个元素之前是没有元素的
//NoSuchElementException
//迭代器一开始的时候指针在第一个位置之前
//Object previous = listIterator.previous();
//String s = (String) previous;
//System.out.println(s + ",长度为:" + s.length());
//先将指针移动到最后一个元素的位置后面
//先正着遍历一遍
System.out.println("正着遍历:");
while (listIterator.hasNext()){
Object next = listIterator.next();
String s = (String) next;
System.out.println(s + ",长度为:" + s.length());
}
System.out.println("************************************");
System.out.println("倒着遍历:");
//然后在倒着遍历
while (listIterator.hasPrevious()){ //指针再最后一个元素上,先判断上一个元素是否存在
Object previous = listIterator.previous(); //当前元素赋值,并且指针返回上一个元素
String s = (String) previous; //转型,输出,输出的时候先正着遍历一遍再倒着遍历一遍
System.out.println(s + ",长度为:" + s.length());
}
}
}
//也就是说Object previous()只有在倒着遍历的时候才能派上用场
3、Collection中的size()方法和List中的get()方法相结合遍历
该方法只能用于List集合
查看代码
import java.util.ArrayList;
import java.util.List;
public class ListDemo3 {
public static void main(String[] args) {
//创建集合对象
List list = new ArrayList();
//创建学生对象
Student s1 = new Student("沫子", 45);
Student s2 = new Student("李元浩", 22);
Student s3 = new Student("智勋", 24);
Student s4 = new Student("呆妹", 22);
Student s5 = new Student("简自豪", 21);
//将学生对象添加到集合中
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);
//size()方法和get()方法的结合使用,
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
//向下转型
String s = (String) obj; //等价于Student student = (Student) list.get(i);
System.out.println(s + ",长度为:" + s.length());
}
}
}