集合进阶
1. 集合知识回顾
集合类特点:提供一个存储空间可变的存储模型,存储的数据容量可以随时改变
单列集合(Collection)和双列集合(Map)
体系结构
Set
1.集合概述和特点
- 元素不重复
- 没有索引,不能普通for循环,可以用增强for
package arrays;
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();//HashSet对集合迭代顺序不保证
set.add("hello");
set.add("world");
set.add("java");
//增强for遍历
for(String s :set){
System.out.println(s);
//world
//java
//hello
}
}
}
2. 哈希值
- 哈希值:jdk根据对象的地址或者字符串或者数字,算出来的int类型的数值
- Object类有一个方法:
public int hashCode()
返回对象的哈希值
对象哈希值的特点:
- 默认情况下,不同对象的哈希值不同,可以通过重写hashCode()实现哈希值相同
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
3. HashSet集合概述和特点
-
底层结构,哈希表
-
不保证顺序
-
没索引,不能普通for循环遍历
-
不重复
(呀呦,不和上面一样)
package arrays;
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();//HashSet对集合迭代顺序不保证
set.add("hello");
set.add("world");
set.add("java");
//增强for遍历
for(String s :set){
System.out.println(s);
//world
//java
//hello
}
}
}
4. HashSet集合保证元素唯一性源码分析
5. 数据结构——哈希表
- 底层采用链表+数组,可以说是一个元素为链表的数组
HashSet存储学生信息
- 重写hashCode()和equals()
package arrays;
import java.util.Objects;
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
package arrays;
import java.util.HashSet;
public class SetDemo {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<>();//
Student s1 = new Student("wts", 20);
Student s2 = new Student("xwb", 30);
Student s3 = new Student("cyc", 40);
Student s4 = new Student("cyc", 40);//重复
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
//增强for遍历
for (Student s : hs) {
System.out.println(s.getName() + "," + s.getAge());
//xwb,30
//cyc,40
//wts,20
}
}
}
6. LinkedHashSet集合概述和特点
- 哈希表和链表实现的Set接口,具有可预测的迭代顺序
- 由链表保证元素有序,(存储和取出顺序一致)
- 哈希表保证唯一
package arrays;
import java.util.LinkedHashSet;
public class SetDemo {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("hello");
linkedHashSet.add("world");
linkedHashSet.add("java");
for (String s : linkedHashSet) {
System.out.println(s);
//hello
//world
//java
}
}
}
7. TreeSet集合概述和特点
- 元素有序,不是存储和取出顺序一致,而是按照一定的规则进行排序,取决于构造方法
TreeSet():根据其元素自然排序进行排序
TreeSet(Comparator comparator):根据指定比较器进行排序 - 没有索引,不能普通for
- 不重复
注意:所有基本类型存储是用它对应的包装类
package arrays;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
//创建对象 (基本数据类型创建的对象用它的包装类)
TreeSet<Integer> ts = new TreeSet<>();
ts.add(50);
ts.add(40);
ts.add(10);
ts.add(30);
ts.add(20);
ts.add(10);//重复不录入
for (Integer i : ts) {
System.out.println(i);//自然排序
//10
//20
//30
//40
//50
}
}
}
8. 自然排序Comparator的使用
- 用TreeSet集合存储自定义对象时,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让所有元素所属的类实现Comparable接口,重写comparableTo()方法
- 重写是按照要求
案例:存储学生对象,年龄从小到达输出
package arrays;
public class Student implements Comparable<Student> { //学生类实现Comparable接口
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 int compareTo(Student s) {
//return 0;//0只添加了第一个(认为s2和s1是同一个元素 );1升序存储;-1降序存储
//按照年龄从小到大
int num = this.age - s.age;//this表示存入的元素,(拿s2和s1说,s1先存进去了,s2后存进去,this就是s2// )
//int num = s.age - this.age
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
package arrays;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
//创建对象 (基本数据类型创建的对象用它的包装类)
//按照年龄从小到大排序
TreeSet<Student> ts = new TreeSet<>();
Student s1 = new Student("wts", 20);
Student s2 = new Student("xwb", 60);
Student s3 = new Student("wyx", 30);
Student s4 = new Student("cyc", 40);
Student s5 = new Student("lx", 20);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
for (Student s : ts) {
System.out.println(s.getName() + ',' + s.getAge());
// lx,20
// wts,20
// wyx,30
// cyc,40
// xwb,60
}
}
}
比较器排序Comparator的使用
- 就是让集合构造方法接受Comparator的实现类对象,重写方法(匿名内部类)
package arrays;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {//匿名内部类
@Override
public int compare(Student s1, Student s2) {
//this.age - s.age
//s1,s2
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
//创建学生对象
//按照年龄从小到大排序
Student s1 = new Student("wts", 20);
Student s2 = new Student("xwb", 60);
Student s3 = new Student("wyx", 30);
Student s4 = new Student("cyc", 40);
Student s5 = new Student("lx", 20);
Student s6 = new Student("lx", 20);//无效
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
for (Student s : ts) {
System.out.println(s.getName() + ',' + s.getAge());
// lx,20
// wts,20
// wyx,30
// cyc,40
// xwb,60
}
}
}
package arrays;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {//匿名内部类
@Override
public int compare(Student s1, Student s2) {
//this.age - s.age
//s1,s2
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
//创建学生对象
//按照年龄从小到大排序
Student s1 = new Student("wts", 20);
Student s2 = new Student("xwb", 60);
Student s3 = new Student("wyx", 30);
Student s4 = new Student("cyc", 40);
Student s5 = new Student("lx", 20);
Student s6 = new Student("lx", 20);//无效
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
for (Student s : ts) {
System.out.println(s.getName() + ',' + s.getAge());
// lx,20
// wts,20
// wyx,30
// cyc,40
// xwb,60
}
}
}
案例:比较器输出学生成绩(条件:总分降序,语文成绩,姓名)
package arrays;
public class Student {
private String name;
private int chinese;
private int math;
public Student() {
}
public Student(String name, int chinese, int math) {
this.name = name;
this.chinese = chinese;
this.math = math;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getsum() {
return this.chinese + this.math;
}
}
package arrays;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//创建对象,运用比较器删选
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件
int num = s2.getsum() - s1.getsum();
//次要条件
int num2 = num==0?s1.getChinese()-s2.getChinese():num;
int num3 = num2==0?s1.getName().compareTo(s2.getName()):num2;
return num3;
}
});
//创建学生
Student s1 = new Student("wts",80,90);
Student s2 = new Student("wts",84,82);
Student s3 = new Student("wts",52,63);
Student s4 = new Student("wts",55,46);
Student s5 = new Student("wts",76,43);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
for(Student s:ts){
System.out.println(s.getName()+','+s.getChinese()+','+s.getMath()+','+s.getsum());
}
}
}
案例:随机数集合输出
package arrays;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//创建set对象
//Set<Integer> set = new HashSet<>();//无序输出
Set<Integer> set = new TreeSet<>();//升序输出
//创建random随机数
Random r = new Random();
//判断集合长度是否小于10,产生一个随机数增加到
while (set.size()< 10){
set.add(r.nextInt(20)+1); //[1,21) 1~20之间的数
}
for(Integer i:set){
System.out.println(i);
}
}
}
泛型
本质:参数化类型,使用或者调用传入具体类型
格式
- <类型>,形参
- <类型1,类型2> , 形参
- 将来具体调用时给定的类型可以看作实参,并且实参只能是引用数据类型
- 把运行问题提前到编译期间,避免强制转换
1. 泛型类
感觉是模板
package arrays;
public class Generic <T>{
private T t;
public Generic() {
}
public Generic(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
package arrays;
public class Demo {
public static void main(String[] args) {
Generic<String> s = new Generic<>();
s.setT("wts");
Generic<Integer> i = new Generic<>();
i.setT(20);
System.out.println(s.getT() + ',' + i.getT());
}
}
2. 泛型方法
package arrays;
public class Generic {
public <T> void show(T t){
System.out.println(t);
}
}
package arrays;
public class Demo {
public static void main(String[] args) {
Generic g = new Generic();
g.show("wts");//wts
g.show(20);//20
g.show(12.25);//12.25
g.show(true);//true
}
}
3. 泛型接口
package arrays;
//泛型接口
public interface Generic <T> {
void show(T t);
}
package arrays;
//接口的实现泛型类
public class GenericImpl<T> implements Generic<T>{
@Override
public void show(T t) {
System.out.println(t);
}
}
package arrays;
/*测试*/
public class Demo {
public static void main(String[] args) {
Generic<String> g = new GenericImpl<>();
g.show("wts");//wts
Generic<Integer> g2 = new GenericImpl<>();
g2.show(200);//200
}
}
4.类型通配符
为了表示各种泛型List的父类,用类型通配符
-
类型通配符<?>
-
List<?>:表示元素类型位置的List,他的元素可以匹配任何的类型
-
仅表示各种泛型List的父类,不能添加元素
不希望List<?>是任何泛型List的父类,只希望是一个泛型List的父类,用类型通配符上限
- <? extends 类型>
List<? extends Number>
表示Number或其子类
类型通配符下限
- <? super 类型>
List<? super Number>
表示Number或其父类
感觉没啥用
5. 可变参数
- 可变参数,其实是个数组
- 如果一个方法有多个参数,包含可变参数,可变参数放最后
public static int sum(int b,int ...a) {...}
package arrays;
public class Demo {
public static void main(String[] args) {
System.out.println(sum(10, 20, 30));//60
System.out.println(sum(10, 10, 10, 10, 10));//50
}
//public static int sum(int b ,int... a) {
public static int sum(int... a) {
int n = 0;
for (int i : a) {//增强for 不太熟啊!
n += i;
}
return n;
}
}
- Arrays工具类有一个静态的方法
public static List asList(T …a):返回指定数组支持的固定大小的列表。不能增删,只能改
List<String> list = Arrays.asList("hello","world","java");//[hello, world, java]
- List接口中有一个静态的方法
public static List of <E …elements>:返回含任意元素的不变的列表
不能增删改
List<String> list1 = List.of("wen","tian","sheng");//[wen,tian,sheng]
- Set接口有一个静态的方法
public static Set of <E …elements>:返回一个任意数量的不可变的集合不能增删改,不能重复
Set<String> set = Set.of("wen", "tian", "sheng");//[tian, wen, sheng]
System.out.println(set);
Map集合
1. Map集合的概述和使用
概述:
- Interface Map< K, V>
- 一键一值,不能包含重复的键
- 举例:学生学号和姓名
创建Map集合的对象
- 多态
- 具体实现类HashMap
package arrays;
import java.util.HashMap;
import java.util.Map;
public class Demo {
public static void main(String[] args) {
//创建集合对象
Map<String,String> map = new HashMap<>();
//V put<K,V>,指定元素和指定键关联,第一次赋值,第二次用就是覆盖
map.put("cczu001","wts");
map.put("cczu002","cyc");
map.put("cczu003","wyx");
map.put("cczu001","???");//覆盖了上一个键
//输出集合对象
System.out.println(map);
//{cczu002=cyc, cczu001=???, cczu003=wyx}
}
}
2. Map集合的基本功能
3. Map集合的获取功能
package arrays;
import java.util.*;
public class Demo {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
map.put("001", "零零一");
map.put("002", "零零二");
map.put("003", "零零三");
//根据键获取值
System.out.println(map.get("001"));//零零一
//获取所有键
Set<String> keyset = map.keySet();
for (String s : keyset) {
System.out.print(s + ' ');//001 002 003
}
System.out.println();
//获取所有值
Collection<String> values = map.values();
for (String ss : values) {
System.out.print(ss + ' ');//零零一 零零二 零零三
}
}
}
4. Map遍历
4.1 方式1
package arrays;
import java.util.*;
public class Demo {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
map.put("001", "零零一");
map.put("002", "零零二");
map.put("003", "零零三");
//遍历
Set<String> keyset = map.keySet();
for (String key : keyset) {
String value = map.get(key);
System.out.println(key + ',' + value);
}
//001,零零一
//002,零零二
//003,零零三
}
}
4.2 方式2
package arrays;
import java.util.*;
public class Demo {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
map.put("001", "零零一");
map.put("002", "零零二");
map.put("003", "零零三");
//获取键值对 对象集合
for (Map.Entry<String, String> me : map.entrySet()) {
System.out.print(me.getKey());
System.out.print(',');
System.out.println(me.getValue());
//001,零零一
// 002,零零二
// 003,零零三
}
}
}