增强for循环
for(集合/数组中元素的数据类型 变量名 : 集合/数组名) {
// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}
注意:
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
基础练习
//增强for循环
ArrayList<String> ll = new ArrayList<String>();
ll.add("aaa");
ll.add("bbb");
ll.add("ccc");
ll.add("ddd");
//一个类的对象如果可以使用增强for循环,前提是类必须实现Iterable接口
for (String s : ll) {
System.out.println(s);
}
如果希望获取当前数据的索引,就只能够自己定义变量,去统计了,如下:
int index = 0;
for (String s : ll) {
index++;
System.out.println(s);
}
遍历学生案例
用一个集合 存放学生对象,用增强for循环遍历打印所有学生信息,再用迭代器方式遍历
//创建集合
ArrayList<Student> list = new ArrayList<>();
//添加学生
list.add(new Student("zhangsan", 18));
list.add(new Student("zhangsan1", 19));
list.add(new Student("zhangsan2", 12));
list.add(new Student("zhangsan3", 13));
list.add(new Student("zhangsan4", 14));
list.add(new Student("zhangsan5", 15));
list.add(new Student("zhangsan6", 16));
//增强for循环遍历学生
for (Student stu : list) {
System.out.println(stu.getName() + "-------" + stu.getAge());
}
//用迭代器遍历
//获取迭代器
Iterator<Student> iter = list.iterator();
while (iter.hasNext()) {
//获取当前的学生对象
Student stu = iter.next();
System.out.println(stu.getName() + "-------" + stu.getAge());
}
普通for循环和增强for
- 增强for 只是把集合中所有数据遍历出来
- 普通for 因为控制条件是我们自己写的,可以控制循环的过程
List
- 集合存取有序: 数据是按添加的顺序存储的,取的时候,可以使用索引按存储的顺序取出
- 可以添加重复数据
特有方法介绍
方法名 | 描述 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
练习代码
List<String> list = new ArrayList();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
System.out.println(list);
//在索引2的位置添加数据xx
list.add(2, "xx");
System.out.println(list);
//删除索引为2的位置的数据 返回被删除的数据xx
String str01 = list.remove(2);
System.out.println(list);
System.out.println(str01);
//把索引为3的位置的数据 改为ff 返回被修改的dd
String str02 = list.set(3, "ff");
System.out.println(list);
System.out.println(str02);
//获取索引0位置的数据
String str03 = list.get(0);
System.out.println(str03);
栈
- 进栈(压栈) 出栈(弹栈)
- 先进后出
队列:
- 先进先出
数组 (数据的顺序存储方式)
- 查询快 增删慢
链表 (数据的链式存储方式)
- 增删快 查询慢
ArrayList源码(了解)
- 数据是存到数组里的
LinkedList
- 底层是链表结构 查询慢 增删快
特有方法
方法名 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
//创建集合对象
List<String> list = new LinkedList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//头部(开始位置)添加数据
list.addFirst("mmm");
list.addFirst("nnn");
System.out.println(list);
//尾部(最后)添加数据
list.addLast("xxx");
list.addLast("yyy");
System.out.println(list);
//获取头部(第一个)数据
String first = list.getFirst();
//获取尾部(最后一个)数据
String last = list.getLast();
System.out.println(first);
System.out.println(last);
//删除头部(第一个)数据
String f = list.removeFirst();
System.out.println(f);
//删除尾部(最后一个)数据
String l = list.removeLast();
System.out.println(l);
System.out.println(list);
定义泛型(重点)
- 常用字母 E T K V S U
T
type表示类型,K,V
代表键值,E
代表element
类中:
格式: 修饰符 class 类名<代表泛型的变量> { }
案例
- 定义一个学生类 定义一个属性 age,希望age的类型可以是Integer也可以是String
代码
- 1 定义学生类 在类的后面添加泛型
<T>
,在定义age属性和相关getset方法时,就使用T - 2 测试 创建2个学生类对象, 分别传入不同的泛型Integer和String
可以定义多个泛型,逗号分开
类代码 两个泛型T 和E
- T装饰age的类型 E装饰height的类型
//类中定义泛型 <T>
public class Student01<T, E> {
//这里使用泛型T
T age;
E height;
public T getAge() {
return age;
}
public void setAge(T age) {
this.age = age;
}
public E getHeight() {
return height;
}
public void setHeight(E height) {
this.height = height;
}
}
测试代码
//创建学生对象 传入泛型 具体类型Integer和Double
Student01<Integer, Double> stu01 = new Student01<>();
stu01.setAge(10);
stu01.setHeight(10.5);
//创建学生对象 传入泛型 具体类型String
Student01<String, String> stu02 = new Student01<>();
stu02.setAge("20");
stu02.setHeight("50");
方法中(要配合参数使用,比较少使用)
格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
语法练习
代码
- 在show方法上定义泛型 ,在参数中 给age定义类型时使用了泛型
- 一般配合参数使用,在调用的时候,传入什么类型, T就是什么类型
public class AA {
//方法中定义的泛型
//一般配合参数使用,在调用的时候,传入什么类型, T就是什么类型
public <T> void show(T age) {
T a = age;
System.out.println(a);
}
}
测试
public class AA {
//方法中定义的泛型
//一般配合参数使用,在调用的时候,传入什么类型, T就是什么类型
public <T> void show(T age) {
T a = age;
System.out.println(a);
}
}
接口中(重点)
格式:修饰符 interface 接口名<代表泛型的变量> { }
接口和实现类代码
- 定义了一个接口 里面定义了泛型
- 两个实现类
//定义接口中的泛型T 在方法中使用了这个T定义参数e
interface inter1<T> {
public void show(t e, int a);
}
//1 写一个类继承接口inter1的时候 就把泛型确定好,比如这里是String,那么show方法中的参1也就是String
class A implements inter1<Integer> {
@Override
public void show(t e, int a) {
System.out.println(e + "----" + a);
}
}
//2 B后面的E是给类定义了一个泛型
// inter1后面的E是使用了类的泛型,把它再传给接口inter1里的泛型T
// show方法是对接口的抽象方法的实现,参数里的E 和Inter1<E>这里的E保持一致
// method参数里的E只是使用了类的泛型E
class B<E> implements inter1<E> {
@Override
public void show(t e, int a) {
System.out.println(e + "----" + a);
}
public void method(E height) {
System.out.println(height);
}
}
测试方法
public static void main(String[] args) {
A a = new A();
a.show(10, 20);
B<String> b = new B<>();
b.show("aaa", 100);
}
类型通配符 ?
类型通配符: <?>
- ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
- 但是ArrayList就不能添加数据了,获取出来的也是Object类型
- 使用通配符的集合不能够修改数据了
代码
- showList方法,接收一个集合类型的数据,可以打印结合中所有数据,但不知道集合里具体类型,索引泛型写了?
- main方法中 创建了两个不同类型的集合对象,传入测试
public static void main(String[] args) {
ArrayList<String> list01 = new ArrayList<>();
list01.add("aa");
list01.add("bb");
list01.add("cc");
showList(list01);
ArrayList<Boolean> list02 = new ArrayList<>();
list02.add(true);
list02.add(false);
list02.add(false);
showList(list02);
}
public static void showList(ArrayList<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
类型通配符上限: <? extends 类型> (源码中常见)
- ArrayList<? extends Number>: 它表示的类型是Number或者其子类型
类型通配符下限: <? super 类型> (用的很少)
- ArrayList <? super Number>: 它表示的类型是Number或者其父类型
代码
public static void main(String[] args) {
ArrayList<Integer> list01 = new ArrayList<>();
list01.add(11);
list01.add(22);
showList(list01);
ArrayList<Double> list02 = new ArrayList<>();
list02.add(1.5);
list02.add(5.1);
showList(list02);
}
//<? extends Number>表示只能传入 Number类型的对象 或者是子类的对象
public static void showList(ArrayList<? extends Number> list) {
for (Number number : list) {
}
}
//传入的必须是Number类型的对象或者是Number的父类的对象 用的极少极少极少极少极少
public static void showList1(ArrayList<? super Number> list) {
for (Object o : list) {
}
}
set集合
TreeSet HashSet
特点
- 1 不能存储重复数据
- 2 没有索引,不能使用普通的for循环。 增强for和迭代器都可以
- 3 存取顺序不一致
基本代码练习
//创建集合对象
Set<String> set = new TreeSet<>();
//添加数据 注意这里重复添加了ddd和bb 第2个ddd和bb不会添加成功
set.add("ccc");
set.add("ddd");
set.add("bb");
set.add("bb");
set.add("ddd");
set.add("ee");
set.add("aaa");
// for (int i = 0; i < set.size(); i++) {
// //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
// }
//迭代器的方式 遍历
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("-----------------------------------");
//增强for遍历
for (String s : set) {
System.out.println(s);
}
treeset (自然排序是重点掌握)
- 会给数据排序
TreeSet<Integer> set = new TreeSet<>();
set.add(50);
set.add(30);
set.add(70);
set.add(60);
set.add(20);
set.add(100);
System.out.println(set);
添加学生对象 报错
- 原因是学生类 没有实现Comparable接口
解决错误 (自然排序)
给学生实现Comparable接口
- 因为后面要比较的是学生对象,索引接口那里添加具体的泛型的类型为Student
- 这里重写的compareTo方法里,是按年龄排序,后续再解释原理
再次运行刚才添加学生的报错的代码
- 发现可以按照年龄排序了
学生按年龄排序图
需求 先按你年龄排序 如果年龄相等 按姓名排序
- 修改Student类里 的compareTo方法
- 1先比较年龄 得到一个值result
- 2 result如果等于0 说明年龄相等 再比较姓名
@Override
public int compareTo(Student o) {
int result = this.age - o.age;
//按照年龄从小到大排序 如果年龄相等 按照名字排序
return result == 0 ? this.name.compareTo(o.name) : result;
//return this.age - o.age;
//return o.age - this.age;
}
测试
TreeSet<Student> ts = new TreeSet<>();
ts.add(new Student("zs", 24));
ts.add(new Student("lisi", 19));
ts.add(new Student("xiaom", 30));
ts.add(new Student("daming", 50));
ts.add(new Student("wangwu", 25));
ts.add(new Student("hehe", 30));
ts.add(new Student("daming", 50));
System.out.println(ts);
Comparator比较器方式排序
为什么用比较器?
- 1我有一个需求,两个集合,各存几个学生,一个集合希望按照年龄从小到大,另外一个希望从大到小
- 用自然排序,只能再学生类中的compareTo中写一种规则,索引需要用比较器
- 比较器可以在创建集合的时候,传入到集合中,一个比较器对象可以制定一种规则。
- 2 有一个需求,希望集合中的字符串按照从大到小排序,那么使用自然排序,得修改String的代码
- 只能用比较器排序
比较器排序Comparator的使用(重点掌握)
- 案例需求
- 两个集合,各存3个学生
- 一个集合按照年龄从小到大排序,另外一个按照年龄从大到小排序
- 实现步骤
- 创建TreeSet集合对象时利用构造方法传入比较器对象。
- 比较器对象以匿名内部类或者lambda表达式的形式传入,重写compare(T o1,T o2)方法,集合就会按照compare方法的规则排序
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
Student类代码
package com.itheima.test1;
public class Student implements Comparable<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 "{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
return 0;
}
}
测试类代码
- 创建了两个集合set1和set2,分别用匿名内部类和lambda的方式传入的比较器对象
- 注意set2中的lambda方式,由于存在泛型,所以添加
(Comparator<Student>)
public static void main(String[] args) {
//按照年龄从小到大排
TreeSet<Student> ts01 = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
});
ts01.add(new Student("zs", 24));
ts01.add(new Student("xiaom", 30));
ts01.add(new Student("lisi", 19));
ts01.add(new Student("daming", 50));
ts01.add(new Student("wangwu", 25));
ts01.add(new Student("hehe", 30));
ts01.add(new Student("daming", 50));
System.out.println(ts01);
//按照年龄从大到小排
TreeSet<Student> ts02 = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.age - o1.age;
}
});
ts02.add(new Student("zs", 24));
ts02.add(new Student("xiaom", 30));
ts02.add(new Student("lisi", 19));
ts02.add(new Student("daming", 50));
ts02.add(new Student("wangwu", 25));
ts02.add(new Student("hehe", 30));
ts02.add(new Student("daming", 50));
System.out.println(ts02);
}
- lambda实现
//按照年龄从小到大排
TreeSet<Student> ts01 = new TreeSet<>((o1, o2) -> o1.age - o2.age);
ts01.add(new Student("zs", 24));
ts01.add(new Student("xiaom", 30));
ts01.add(new Student("lisi", 19));
ts01.add(new Student("daming", 50));
ts01.add(new Student("wangwu", 25));
ts01.add(new Student("hehe", 30));
ts01.add(new Student("daming", 50));
System.out.println(ts01);
//按照年龄从大到小排
TreeSet<Student> ts02 = new TreeSet<>((o1, o2) -> o2.age - o1.age);
ts02.add(new Student("zs", 24));
ts02.add(new Student("xiaom", 30));
ts02.add(new Student("lisi", 19));
ts02.add(new Student("daming", 50));
ts02.add(new Student("wangwu", 25));
ts02.add(new Student("hehe", 30));
ts02.add(new Student("daming", 50));
System.out.println(ts02);
两种比较方式总结
- 两种比较方式小结
- 自然排序: 需要比较的对象的类要实现Comparable接口,重写compareTo方法,根据返回值进行排序
- 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
- 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
- 两种方式中关于返回值的规则
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边