(1)Collection接口
(2)Map接口
(3)其他实用类:
(4)数组和集合的区别
(5)单列集合:Collection
(6)双列集合:Map
(1)Collection集合概述
(2)创建Collection集合的对象
(3)Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 把给定的对象在当前的集合中删除 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
public class CollectionDemo01 {
public static void main(String[] args) {
/**
* public boolean add(E e) 添加
* public void clear() 清空
* public boolean remove(E e) 删除
* public boolean contains(Object obj) 判断是否包含
* public boolean isEmpty() 判断是否为空
* public int size() 集合长度
* 注意点:
* Collection是一个接口,我们不能直接创建他的对象。
* 所以,现在我们学习他的方法时,只能创建他实现类的对象。
* 实现类:ArrayList
* 目的:为了学习Collection接口里面的方法
*/
Collection<String> coll = new ArrayList<>();
/**
* 1.添加元素
* 细节1:如果要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的。
* 细节2:如果要往Set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功。
* 如果当前要添加的元素已经存在,方法返回false,表示添加失败。因为Set系列的集合不允许重复。
*/
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
System.out.println(coll);
//2.清空
//coll.clear();
/**
* 3.删除
* 细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除。
* 细节2:方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false,如果要删除的元素不存在,就会删除失败。
*/
System.out.println(coll.remove("aaa"));
System.out.println(coll);
/**
* 4.判断元素是否包含
* 细节:底层是依赖equals方法进行判断是否存在的。
* 所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法。
*/
boolean result1 = coll.contains("bbb");
System.out.println(result1);
//5.判断集合是否为空
boolean result2 = coll.isEmpty();
System.out.println(result2);//false
//6.获取集合的长度
coll.add("ddd");
int size = coll.size();
System.out.println(size);//3
}
}
需求:如果同姓名和同年龄,就认为是同一个学生。
因为contains方法在底层依赖equals方法判断对象是否一致的。
如果存的是自定义对象,没有重写equals方法,那么默认使用Object类中的equals方法进行判断,而Object类中equals方法,依赖地址值进行判断。
所以,需要在自定义的Javabean类中,重写equals方法就可以了。
(1)自定义对象Student,需要有重写equals方法
这里只需要equals(),不需要hashCode(),所以删掉hashCode()
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;
}
// 重写equals方法(可以使用idea的快捷键快速实现)
@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);
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
(2)测试
public class CollectionDemo02 {
public static void main(String[] args) {
//1.创建集合的对象
Collection<Student> coll = new ArrayList<>();
//2.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
//3.把学生对象添加到集合当中
coll.add(s1);
coll.add(s2);
coll.add(s3);
//4.判断集合中某一个学生对象是否包含
Student s4 = new Student("zhangsan",23);
Student s5 = new Student("zhangsan123",23);
/**
* 因为contains方法在底层依赖equals方法判断对象是否一致的。
* 如果存的是自定义对象,没有重写equals方法,那么默认使用Object类中的equals方法进行判断,而Object类中equals方法,依赖地址值进行判断。
*
* 需求:如果同姓名和同年龄,就认为是同一个学生。
* 所以,需要在自定义的Javabean类中,重写equals方法就可以了。
*/
System.out.println(coll.contains(s4));
System.out.println(coll.contains(s5));
}
}
迭代器不依赖下标。
Java中为什么要有迭代器Iterator?
- 统一访问方式: 迭代器提供了一种一致的访问方式,无论集合的具体实现是什么,都可以通过相同的接口方法来访问元素,这样简化了代码并提高了代码的可读性。
- 隐藏集合内部实现: 迭代器使得客户端代码无需了解集合内部的实现细节,只需知道如何使用迭代器的方法,从而提高了代码的封装性。如果集合的实现发生变化,只需调整迭代器的实现而不需要修改客户端代码。
- 支持并发访问: 在多线程环境中,使用迭代器遍历集合是一种安全的方式,因为它提供了一种在迭代过程中防止其他线程修改集合的机制。一些集合类的迭代器实现,如
ArrayList
的Iterator
,不支持在迭代过程中修改集合,而在迭代器创建时会记录集合的修改次数,如果在迭代过程中修改了集合,就会抛出ConcurrentModificationException
异常。- 降低耦合度: 使用迭代器可以将集合的遍历逻辑和具体的集合实现分离开来,这样可以降低代码的耦合度。客户端代码只需要通过迭代器访问元素,而不需要关心集合的内部结构。
(1)迭代器介绍
(2)Iterator中的常用方法
迭代器遍历相关的三个方法:
Iterator<E> iterator() :获取一个迭代器对象
boolean hasNext() :判断当前指向的位置是否有元素
E next() :获取当前指向的元素并移动指针
(3)Collection集合的遍历
public class CollectionDemo03 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//2.获取迭代器对象
//迭代器就好比是一个箭头,默认指向集合的0索引处
Iterator<String> it = coll.iterator();
//3.利用循环不断的去获取集合中的每一个元素
while(it.hasNext()){
//4.next方法的两件事情:获取元素并移动指针
String str = it.next();
System.out.println(str);
}
}
}
(4)迭代器中删除的方法
void remove(): 删除迭代器对象当前指向的元素
public class CollectionDemo03 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//2.获取迭代器对象
//迭代器就好比是一个箭头,默认指向集合的0索引处
Iterator<String> it = coll.iterator();
//3.利用循环不断的去获取集合中的每一个元素
while(it.hasNext()){
//4.next方法的两件事情:获取元素并移动指针
String str = it.next();
if("aaa".equals(str)){
// 指向谁,那么此时就删除谁.
it.remove();
}
}
System.out.println(coll);
}
}
(5)迭代器遍历注意点
迭代器的细节注意点:
1.报错NoSuchElementException
2.迭代器遍历完毕,指针不会复位
3.循环中只能用一次next方法
4.迭代器遍历时,不能用集合的方法进行增加或者删除。如果要删除,那么可以用迭代器提供的remove方法进行删除;如果要添加,暂时没有办法,但在list集合中有列迭代器ListIterator可以在遍历的时候添加元素。
public class CollectionDemo04 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//2.获取迭代器对象
//迭代器就好比是一个箭头,默认指向集合的0索引处
Iterator<String> it = coll.iterator();
//3.利用循环不断的去获取集合中的每一个元素
while(it.hasNext()){
//4.next方法的两件事情:获取元素并移动指针
String str = it.next();
System.out.println(str);
}
//当上面循环结束之后,迭代器的指针已经指向了最后没有元素的位置
//System.out.println(it.next()); //报错NoSuchElementException
//迭代器遍历完毕,指针不会复位
System.out.println(it.hasNext()); // false
//如果我们要继续第二次遍历集合,只能再次获取一个新的迭代器对象
Iterator<String> it2 = coll.iterator();
while(it2.hasNext()){
String str = it2.next();
System.out.println(str);
}
}
}
迭代器删除元素,要使用迭代器的方法,不能使用集合的方法
public class CollectionDemo05 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
coll.add("eee");
//2.获取迭代器对象
//迭代器就好比是一个箭头,默认指向集合的0索引处
Iterator<String> it = coll.iterator();
//3.利用循环不断的去获取集合中的每一个元素
while(it.hasNext()){
//4.next方法的两件事情:获取元素,并移动指针
String str = it.next();
if("bbb".equals(str)){
//coll.remove("bbb"); // 报错ConcurrentModificationException
it.remove();
}
}
System.out.println(coll);
}
}
(1)介绍
(2)格式
for(集合/数组中元素的数据类型 变量名 : 集合/数组名) {
// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}
(3)代码
public class CollectionDemo06 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
/**
* 2.利用增强for进行遍历
* 注意点:s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
* 数据类型一定是集合或者数组中元素的类型,coll就是要遍历的集合或者数组
*/
for(String s : coll){
System.out.println(s);
s = "qqq"; // 并不与会修改集合中的数据
System.out.println(s);
}
System.out.println(coll);//zhangsan lisi wangwu
}
}
可以先写成匿名内部类的方式再改为lambda表达式格式。
lambda表达式遍历:
default void forEach(Consumer<? super T> action):
利用forEach方法,再结合lambda表达式的方式进行遍历
public class CollectionDemo07 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
/**
* 2.利用匿名内部类的形式
* 底层原理:
* 其实也会自己遍历集合,依次得到每一个元素
* 把得到的每一个元素,传递给下面的accept方法
* s依次表示集合中的每一个数据
*/
System.out.println("---------匿名内部类方式---------");
coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("---------lambda表达式方式---------");
// lambda表达式
coll.forEach(s -> System.out.println(s));
}
}
总结:
迭代器:在遍历的过程中需要删除元素,请使用迭代器。
增强for、Lambda:仅仅想遍历,那么使用增强for或Lambda表达式。
(1)List集合的概述
(2)List集合的特点
(1)方法介绍
方法名 | 描述 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
(2)示例代码
public class ListDemo01 {
public static void main(String[] args) {
//1.创建一个集合
List<String> list = new ArrayList<>();
//2.添加元素
list.add("aaa");
list.add("bbb");//1
list.add("ccc");
//3、在此集合中的指定位置插入指定的元素
//细节:原来索引上的元素会依次往后移
list.add(1,"QQQ");
System.out.println(list);
//4、删除指定索引处的元素,返回被删除的元素
String remove = list.remove(0);
System.out.println(remove); // aaa
//5、修改指定索引处的元素,返回被修改的元素
String result = list.set(1, "QQQ");
System.out.println(result); // bbb
System.out.println(list);
//6、返回指定索引处的元素
String s = list.get(2);
System.out.println(s); // ccc
}
}
E remove(int index); // 删除索引index上的元素,这里是基本类型
boolean remove(Object o); // 删除元素o,注意这里是引用类型
public class ListDemo02 {
public static void main(String[] args) {
//1.创建集合并添加元素
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//2.删除元素
/**
* 请问:此时删除的是1这个元素,还是1索引上的元素?为什么?
* 删除的是1索引上的元素
* 因为在调用方法的时候,如果方法出现了重载现象,优先调用实参跟形参类型一致的那个方法。
* 由于1还要装箱才能变为引用类型,而删除索引上的元素不需要装箱
*/
list.remove(1);
System.out.println(list);
//手动装箱,手动把基本数据类型的1,变成Integer类型
Integer i = Integer.valueOf(1);
list.remove(i); // 删除了元素1,而不是索引1上的元素
System.out.println(list);
}
}
(1)迭代器
(2)列表迭代器
(3)增强for
(4)Lambda表达式
(5)普通for循环
代码示例:
public class ListDemo03 {
public static void main(String[] args) {
//创建集合并添加元素
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//1.迭代器
System.out.println("----------1.迭代器------------");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
/**
* 2.增强for
* 下面的变量s,其实就是一个第三方的变量而已。
* 在循环的过程中,依次表示集合中的每一个元素
*/
System.out.println("----------1.迭代器------------");
for (String s : list) {
System.out.println(s);
}
/**
* 3.Lambda表达式
* forEach方法的底层其实就是一个循环遍历,依次得到集合中的每一个元素
* 并把每一个元素传递给下面的accept方法
* accept方法的形参s,依次表示集合中的每一个元素
*/
System.out.println("----------3.匿名内部类方式------------");
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("----------3.Lambda表达式------------");
list.forEach(s->System.out.println(s) );
/**
* 4.普通for循环
* size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素
*/
System.out.println("----------4.普通for循环式------------");
for (int i = 0; i < list.size(); i++) {
//i:依次表示集合中的每一个索引
String s = list.get(i);
System.out.println(s);
}
/**
* 5.列表迭代器
* 获取一个列表迭代器的对象,里面的指针默认也是指向0索引的
* 额外添加了一个方法:在遍历的过程中,可以添加元素
*/
System.out.println("----------5.列表迭代器------------");
ListIterator<String> lit = list.listIterator();
while(lit.hasNext()){
String str = lit.next();
if("bbb".equals(str)){
//在“bbb”后添加元素“qqq”
lit.add("qqq");
}
System.out.println(str);
}
System.out.println(list);
}
}
总结:
- 迭代器遍历:在遍历的过程中需要删除元素,请使用迭代器。
- 列表迭代器:在遍历的过程中需要添加元素,请使用列表迭代器。
- 增强for遍历、Lambda表达式:仅仅想遍历,那么使用增强for或Lambda表达式。
- 普通for:如果遍历的时候想操作索引,可以用普通for。
集合和数组的优势对比:
什么是集合:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
ArrayList集合的特点:长度可以变化,只能存储引用数据类型。
泛型的使用:用于约束集合中存储元素的数据类型
构造方法
方法名 | 说明 |
---|---|
public ArrayList() | 创建一个空的集合对象 |
成员方法
方法名 | 说明 |
---|---|
public boolean add(要添加的元素) | 将指定的元素追加到此集合的末尾 |
public boolean remove(要删除的元素) | 删除指定元素,返回值表示是否删除成功 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
示例代码
public class ArrayListDemo02 {
public static void main(String[] args) {
//1.创建一个集合
ArrayList<String> list = new ArrayList<>();
//2.添加元素
list.add("aaa");
list.add("aaa");
list.add("bbb");
list.add("ccc");
//3.删除元素
/* boolean result1 = list.remove("aaa");
System.out.println(result1);
boolean result2 = list.remove("ddd");
System.out.println(result2);
String str = list.remove(2);
System.out.println(str);*/
//修改元素
/*String result = list.set(1, "ddd");
System.out.println(result);*/
//查询元素
/* String s = list.get(0);
System.out.println(s);*/
//遍历
for (int i = 0; i < list.size(); i++) {
//i 索引
//list.get(i) 元素
String str = list.get(i);
System.out.println(str);
}
}
}
案例需求:创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
代码实现
public class ArrayListDemo3 {
public static void main(String[] args) {
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加元素
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
//3.遍历
//快捷键: list.fori 正向遍历
//list.forr 倒着遍历
System.out.print("[");
for (int i = 0; i < list.size(); i++) {
//i 依次表示集合里面的每一个索引
if(i == list.size() - 1){
//最大索引
System.out.print(list.get(i));
}else{
//非最大索引
System.out.print(list.get(i) + ", ");
}
}
System.out.print("]");
}
}
案例需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
代码实现
public class ArrayListDemo4 {
public static void main(String[] args) {
//1.创建集合对象,用来存储数据
ArrayList<Student> list = new ArrayList<>();
//2.创建学生对象
Student s1 = new Student("zhangsan",16);
Student s2 = new Student("lisi",15);
Student s3 = new Student("wangwu",18);
//3.把学生对象添加到集合中
list.add(s1);
list.add(s2);
list.add(s3);
//4.遍历
for (int i = 0; i < list.size(); i++) {
//i 依次表示集合中的每一个索引
Student stu = list.get(i);
System.out.println(stu.getName() + ", " + stu.getAge());
}
}
}
需求:
代码示例:
public class Test3 {
public static void main(String[] args) {
//1.创建集合对象
ArrayList<User> list = new ArrayList<>();
//2.创建用户对象
User u1 = new User("1001", "zhangsan", "123456");
User u2 = new User("1002", "lisi", "1234");
User u3 = new User("1003", "wangwu", "1234qwer");
//3.把用户对象添加到集合当中
list.add(u1);
list.add(u2);
list.add(u3);
//4.调用方法,通过id获取对应的索引
int index = getIndex(list, "1001");
System.out.println(index);
}
public static int getIndex(ArrayList<User> list, String id) {
//遍历集合得到每一个元素
for (int i = 0; i < list.size(); i++) {
User u = list.get(i);
String uid = u.getId();
if(uid.equals(id)){
return i;
}
}
return -1;
}
}
需求:
代码实现:
public class Test4 {
public static void main(String[] args) {
//1.定义集合
ArrayList<User> list = new ArrayList<>();
//2.创建对象
User u1 = new User("1001","zhangsan","123456");
User u2 = new User("1002","lisi","12345678");
User u3 = new User("1003","wangwu","1234qwer");
//3.把用户对象添加到集合当中
list.add(u1);
list.add(u2);
list.add(u3);
//4.调用方法,查询id是否存在
boolean result = contains(list, "1001");
System.out.println(result);
}
//定义在测试类中的方法需要加static
public static boolean contains(ArrayList<User> list, String id){
for (int i = 0; i < list.size(); i++) {
User u = list.get(i);
String uid = u.getId();
if(id.equals(uid)){
return true;
}
}
return false;
}
}
数组结构
查询快、增删慢
队列结构
查询慢、增删快
ArrayList集合
底层是数组结构实现,查询快、增删慢
LinkedList集合
底层是双链表结构实现,查询慢、增删快
(1)特有方法
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
(2)示例代码
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.println("开始列表:"+list);
// 1、public void addFirst(E e)在该列表开头插入指定的元素
method1(list);
// 2、public void addLast(E e)将指定的元素追加到此列表的末尾
method2(list);
/**
* 3、
* public E getFirst()返回此列表中的第一个元素
* public E getLast()返回此列表中的最后一个元素
*/
method3(list);
/**
* 4、
* public E removeFirst() 从此列表中删除并返回第一个元素
* public E removeLast() 从此列表中删除并返回最后一个元素
*/
method4(list);
}
private static void method4(LinkedList<String> list) {
String first = list.removeFirst();
System.out.println("移除列表第一个元素"+first);
String last = list.removeLast();
System.out.println("移除列表最后一个元素"+last);
System.out.println("最终列表:"+list);
}
private static void method3(LinkedList<String> list) {
String first = list.getFirst();
String last = list.getLast();
System.out.println("获取列表第一个元素"+first);
System.out.println("获取列表最后一个元素"+last);
}
private static void method2(LinkedList<String> list) {
list.addLast("www");
System.out.println("在列表末尾添加元素“www”:"+list);
}
private static void method1(LinkedList<String> list) {
list.addFirst("qqq");
System.out.println("在列表头部添加元素“qqq”:"+list);
}
}
Set集合的实现类:
Set接口中的方法上基本上与Collection的API一致。
利用Set系列的集合,添加字符串,并使用多种方式遍历。
- 迭代器
- 增强for
- Lambda表达式
public class SetDemo {
public static void main(String[] args) {
//1.创建一个Set集合的对象
Set<String> s = new HashSet<>();
/**
* 2,添加元素boolean add(E e);
* 如果当前元素是第一次添加,那么可以添加成功,返回true
* 如果当前元素是第二次添加,那么添加失败,返回false
*/
s.add("张三");
s.add("张三");
s.add("李四");
s.add("王五");
//3.打印集合,无序
System.out.println(s);//[李四, 张三, 王五]
//4、迭代器遍历
System.out.println("-------迭代器遍历---------");
Iterator<String> it = s.iterator();
while (it.hasNext()){
String str = it.next();
System.out.println(str);
}
//5、增强for遍历
System.out.println("-------增强for遍历---------");
for (String str : s) {
System.out.println(str);
}
//6、Lambda表达式方式遍历
System.out.println("-------匿名内部类方式遍历---------");
s.forEach(new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
});
System.out.println("-------Lambda表达式方式遍历---------");
s.forEach( str->System.out.println(str));
}
}
没有什么新方法,使用Collection中的方法即可。
重写hashcode:想要根据属性值计算hash值。
重写equals:想要在比较时,比的是对象内部的属性值。
哈希值:对象的整数表现形式
1. 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
2. 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
3. 但是在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
(1)Student.java未重写hashCode方法
public class HashSetDemo01 {
public static void main(String[] args) {
//1.创建对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("zhangsan",23);
/**
* 2.如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
* 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
* Student还没重写hashCode方法
*/
System.out.println(s1.hashCode());//1324119927
System.out.println(s2.hashCode());//990368553
//在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。
//哈希碰撞
System.out.println("abc".hashCode());//96354
System.out.println("acD".hashCode());//96354
}
}
(2)Student.java重写hashCode方法后
// Student.java
@Override
public int hashCode() {
return Objects.hash(name, age);
}
/**
* 2.如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
* 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
*/
System.out.println(s1.hashCode());//-1461067292
System.out.println(s2.hashCode());//-1461067292
在HashSet中比较对象内部属性值需要重写hashCode方法和equals方法
Student.java
@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);
}
测试
需求:创建一个存储学生对象的集合,存储多个学生对象。
要求:学生对象的成员变量值相同,我们就认为是同一个对象
public class HashSetDemo02 {
public static void main(String[] args) {
//1.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("zhangsan",23);
//2.创建集合用来添加学生
HashSet<Student> hs = new HashSet<>();
//3.添加元素
System.out.println(hs.add(s1));
System.out.println(hs.add(s2));
System.out.println(hs.add(s3));
System.out.println(hs.add(s4));
//4.打印集合
System.out.println(hs);
}
}
没有什么新方法,使用Collection中的方法即可。底层基于哈希表,使用双链表记录添加顺序。
有序、不重复、无索引。这里的有序指的是保证存储和取出的元素顺序一致。
代码示例:
对于自定义对象进行不重复添加时,需要重写equals方法和hashCode方法,在Student.java中添加代码
@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);
}
测试
public class LinkedHashSetDemo {
public static void main(String[] args) {
//1.创建4个学生对象,Student需要重写equals方法和hashCode方法
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("zhangsan",23);
//2.创建集合的对象
LinkedHashSet<Student> lhs = new LinkedHashSet<>();
LinkedHashSet<String> lhs2 = new LinkedHashSet<>();
LinkedHashSet<Integer> lhs3 = new LinkedHashSet<>();
//3.添加元素
System.out.println(lhs.add(s3));
System.out.println(lhs.add(s1));
System.out.println(lhs.add(s2));
System.out.println(lhs.add(s4));
System.out.println("----------------------");
System.out.println(lhs2.add("q"));
System.out.println(lhs2.add("q"));
System.out.println(lhs2.add("a"));
System.out.println("----------------------");
System.out.println(lhs3.add(123));
System.out.println(lhs3.add(123));
System.out.println("----------------------");
//4.打印集合
System.out.println(lhs);
System.out.println(lhs2);
System.out.println(lhs3);
}
}
没有什么新方法,使用Collection中的方法即可。TreeSet会默认对元素进行升序排序,底层基于红黑树实现排序。
对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序
对于字符、字符串类型:按照字符在ASC码表中的数字升序进行排序
对于自定义的对象、需要自定义排序的类型有两种方法:
- 方式一:可以使用Javabean类实现Comparable接口
- 方式二:创建集合时,自定义Comparator比较器对象,自定义排序的方法
- 如果方式一和方式二同时存在,以方式二定义的规则为准
存储Integer类型的整数并遍历
public class TreeSetDemo01 {
public static void main(String[] args) {
//1.创建TreeSet集合对象
TreeSet<Integer> ts = new TreeSet<>();
//2.添加元素
ts.add(4);
ts.add(5);
ts.add(1);
ts.add(3);
ts.add(2);
//3.打印集合,会自动进行排序
System.out.println(ts);
//4.遍历集合(三种遍历)
//4.1迭代器
Iterator<Integer> it = ts.iterator();
while(it.hasNext()){
int i = it.next();
System.out.println(i);
}
System.out.println("--------------------------");
//4.2增强for
for (int t : ts) {
System.out.println(t);
}
//4.3 lambda
System.out.println("--------匿名内部类-------");
ts.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
System.out.println("--------lambda-------");
ts.forEach( i-> System.out.println(i));
}
}
(1)案例需求
(2)实现步骤
(3)代码实现
学生类
public int compareTo(Student o) {
return this.getAge() - o.getAge();
}
按年龄递增排序
this:表示当前要添加的元素
o:表示已经在红黑树存在的元素
返回值:
负数:表示当前要添加的元素是小的,存左边
正数:表示当前要添加的元素是大的,存右边
0 :表示当前要添加的元素已经存在,舍弃
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
/**
* 按年龄递增排序
* this:表示当前要添加的元素
* o:表示已经在红黑树存在的元素
* 返回值:
* 负数:表示当前要添加的元素是小的,存左边
* 正数:表示当前要添加的元素是大的,存右边
* 0 :表示当前要添加的元素已经存在,舍弃
*/
@Override
public int compareTo(Student o) {
System.out.println("--------------");
System.out.println("this:" + this);
System.out.println("o:" + o);
//指定排序的规则。只看年龄,我想要按照年龄的升序进行排列
return this.getAge() - o.getAge();
}
}
测试类
public class TreeSetDemo02 {
public static void main(String[] args) {
/*
需求:创建TreeSet集合,并添加3个学生对象
学生对象属性:
姓名,年龄。
要求按照学生的年龄进行排序
同年龄按照姓名字母排列(暂不考虑中文)
同姓名,同年龄认为是同一个人
方式一:
默认的排序规则/自然排序
Student实现Comparable接口,重写里面的抽象方法,再指定比较规则
*/
//1.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("zhaoliu",26);
//2.创建集合对象
TreeSet<Student> ts = new TreeSet<>();
//3.添加元素
ts.add(s3);
ts.add(s2);
ts.add(s1);
ts.add(s4);
System.out.println("--------最终排序结果------");
//4.打印集合
System.out.println(ts);
}
}
(1)案例需求
需求:请自行选择比较器排序和自然排序两种方式;
要求:存入四个字符串, “df”,“c”, “qwer”, “ab”
按照长度排序,如果一样长则按照首字母排序(如果不自定义排序规则,字符串默认根据ASCII排序)
采取第二种排序方式:比较器排序
(2)实现步骤
(3)代码实现
o1:表示当前要添加的元素
o2:表示已经在红黑树存在的元素
返回值规则跟之前是一样的
public class TreeSetDemo03 {
public static void main(String[] args) {
/**
* 创建集合
* o1:表示当前要添加的元素
* o2:表示已经在红黑树存在的元素
* 返回值规则跟之前是一样的
*/
System.out.println("-------先设置排序规则再添加元素:匿名内部类方式-----");
TreeSet<String> t = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 按照长度排序(主要条件)
int i = o1.length() - o2.length();
//如果一样长则按照首字母排序(次要条件)
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
});
// 添加元素
t.add("c");
t.add("ab");
t.add("df");
t.add("qwer");
System.out.println(t);
System.out.println("-------先设置排序规则再添加元素:Lambda表达式方式-----");
TreeSet<String> ts= new TreeSet<>((o1, o2)->{
// 按照长度排序(主要条件)
int i = o1.length() - o2.length();
//如果一样长则按照首字母排序(次要条件)
i = i == 0 ? o1.compareTo(o2) : i;
return i;
});
// 添加元素
ts.add("df");
ts.add("c");
ts.add("qwer");
ts.add("ab");
System.out.println(ts);
TreeSet<String> ts2 = new TreeSet<>();
//添加元素
ts2.add("df");
ts2.add("c");
ts2.add("qwer");
ts2.add("ab");
System.out.println("------不自定义排序规则时,字符串默认根据ASCII排序--------");
System.out.println(ts2);
System.out.println("------先添加元素再设置排序规则:匿名内部类方式--------");
Stream<String> sorted = ts2.stream().sorted(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 按照长度排序(主要条件)
int i = o1.length() - o2.length();
//如果一样长则按照首字母排序(次要条件)
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
});
System.out.println(Arrays.toString(sorted.toArray()));
System.out.println("------先添加元素再设置排序规则:Lambda表达式方式--------");
Stream<String> sorted2 = ts2.stream().sorted((o1, o2)-> {
// 按照长度排序(主要条件)
int i = o1.length() - o2.length();
//如果一样长则按照首字母排序(次要条件)
i = i == 0 ? o1.compareTo(o2) : i;
return i;
});
System.out.println(Arrays.toString(sorted2.toArray()));
}
}
(1)两种比较方式小结
(2)两种方式中关于返回值的规则
(3)练习
需求:创建5个学生对象
属性:(姓名,年龄,语文成绩,数学成绩,英语成绩),
按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英文成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排
如果都一样,认为是同一个学生,不存。
第一种:默认排序/自然排序
第二种:比较器排序
默认情况下,用第一种排序方式,如果第一种不能满足当前的需求,采取第二种方式。这里使用第一种排序方式
Student2.java的自定义排序代码
@Override
public int compareTo(Student2 o) {
int sum1 = this.getChinese() + this.getMath() + this.getEnglish();
int sum2 = o.getChinese() + o.getMath() + o.getEnglish();
//比较两者的总分
int i = sum2 - sum1;
//如果总分一样,就按照语文成绩排序
i = i == 0 ? this.getChinese() - o.getChinese() : i;
//如果语文成绩一样,就按照数学成绩排序
i = i == 0 ? this.getMath() - o.getMath() : i;
//如果数学成绩一样,按照英语成绩排序(可以省略不写)
i = i == 0 ? this.getEnglish() - o.getEnglish() : i;
//如果英文成绩一样,按照年龄排序
i = i == 0 ? this.getAge() - o.getAge() : i;
//如果年龄一样,按照姓名的字母顺序排序
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
测试
public class TreeSetDemo04 {
public static void main(String[] args) {
//1.创建学生对象
Student2 s1 = new Student2("zhangsan",23,90,99,50);
Student2 s2 = new Student2("lisi",24,90,98,50);
Student2 s3 = new Student2("wangwu",25,95,100,30);
Student2 s4 = new Student2("zhaoliu",26,60,99,70);
Student2 s5 = new Student2("qianqi",26,70,80,70);
//2.创建集合
TreeSet<Student2> ts = new TreeSet<>();
//3.添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//4.遍历集合
for (Student2 t : ts) {
int sum = t.getChinese()+t.getMath()+t.getEnglish();
System.out.println(t+"-总分:"+sum);
}
}
}
键值对、键值对对象:Entry对象。
(1)Map集合概述
interface Map<K,V> K:键的类型;V:值的类型
(2)Map集合的特点
(1)方法介绍
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
(2)示例代码
public class MyMapDemo01 {
public static void main(String[] args) {
//1.创建Map集合的对象
Map<String, String> m = new HashMap<>();
/**
* 2.添加元素
* put方法的细节:
* 添加/覆盖
* 在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回null
* 在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。
*/
m.put("郭靖", "黄蓉");
m.put("韦小宝","沐剑屏");
m.put("尹志平","小龙女");
System.out.println("集合初始键值对:"+m);
String value2 = m.put("韦小宝", "双儿");
System.out.println("覆盖键的值会把之前的值返回:"+value2);
//3.删除
String result = m.remove("郭靖");//黄蓉
System.out.println("删除键会返回该键对应的值:"+result);
//4.判断是否包含
boolean keyResult = m.containsKey("郭靖");
System.out.println("是否包含该键:"+keyResult);
boolean valueResult = m.containsValue("小龙女2");
System.out.println("是否包含该值:"+valueResult);
//5.集合是否为空
boolean result2 = m.isEmpty();
System.out.println("集合是否为空:"+result2);
//6.集合长度
int size = m.size();
System.out.println("此时的集合长度:"+size);
System.out.println("此时集合的键值对:"+m);
//7.清空集合
m.clear();
int size2 = m.size();
System.out.println("清空集合后,集合长度:"+size2);
System.out.println("集合最终的键值对:"+m);
}
}
(1)方法介绍
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
Set |
获取所有键值对对象的集合 |
(2)示例代码
public class MyMapDemo05 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//V get(Object key):根据键获取值
System.out.println(map.get("张无忌"));
System.out.println(map.get("张三丰"));
System.out.println("----------------------------");
//Set keySet():获取所有键的集合
Set<String> keySet = map.keySet();
for(String key : keySet) {
System.out.println(key);
}
System.out.println("----------------------------");
//Collection values():获取所有值的集合
Collection<String> values = map.values();
for(String value : values) {
System.out.println(value);
}
}
}
通过键去找值。
(1)步骤分析
(2)代码实现
public class MyMapDemo02 {
public static void main(String[] args) {
//1.创建Map集合的对象
Map<String,String> map = new HashMap<>();
//2.添加元素
map.put("尹志平","小龙女");
map.put("郭靖","穆念慈");
map.put("欧阳克","黄蓉");
//3.通过键找值,获取所有的键,把这些键放到一个单列集合当中
Set<String> keys = map.keySet();
//3.1增强for:遍历单列集合,得到每一个键
System.out.println("------------增强for-----------");
for (String key : keys) {
// 利用map集合中的键获取对应的值
String value = map.get(key);
System.out.println(key + " = " + value);
}
//3.2迭代器
System.out.println("------------迭代器-----------");
Iterator<String> it = keys.iterator();
while (it.hasNext()){
String key = it.next();
String value = map.get(key);
System.out.println(key + " = " + value);
}
//3.3 Lambda表达式
System.out.println("----------匿名内部类---------");
keys.forEach(new Consumer<String>() {
@Override
public void accept(String key) {
System.out.println(key + " = " + map.get(key));
}
});
System.out.println("----------Lambda表达式---------");
keys.forEach(key ->System.out.println(key + " = " + map.get(key)));
}
}
(1)步骤分析
(2)代码实现
public class MyMapDemo03 {
public static void main(String[] args) {
//1.创建Map集合的对象
Map<String, String> map = new HashMap<>();
//2.添加元素
map.put("标枪选手", "马超");
map.put("人物挂件", "明世隐");
map.put("御龙骑士", "尹志平");
//3.Map集合的第二种遍历方式:通过键值对对象进行遍历
//通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//3.1 增强for:遍历entries这个集合,去得到里面的每一个键值对对象
System.out.println("------------增强for-----------");
for (Map.Entry<String, String> entry : entries) {//entry ---> "御龙骑士","尹志平"
//利用entry调用get方法获取键和值
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
//3.2迭代器
System.out.println("------------迭代器-----------");
Iterator<Map.Entry<String, String>> it = entries.iterator();
while (it.hasNext()){
Map.Entry<String, String> itmap = it.next(); // next()获取当前指针所指向的元素,并将指针移到下一个元素
String key = itmap.getKey();
String value = itmap.getValue();
System.out.println(key + "=" + value);
}
//3.3Lambda表达式
System.out.println("------------匿名内部类方式-----------");
entries.forEach(new Consumer<Map.Entry<String, String>>() {
@Override
public void accept(Map.Entry<String, String> stringStringEntry) {
System.out.println(stringStringEntry.getKey() + "=" + stringStringEntry.getValue());
}
});
System.out.println("------------Lambda表达式方式-----------");
entries.forEach(stringStringEntry -> System.out.println(stringStringEntry.getKey() + "=" + stringStringEntry.getValue()));
}
}
default void forEach(BiConsumer<? super K, ? super V> action) // 结合lambda遍历Map集合
代码实现
public class MyMapDemo04 {
public static void main(String[] args) {
//1.创建Map集合的对象
Map<String,String> map = new HashMap<>();
//2.添加元素
map.put("鲁迅","这句话是我说的");
map.put("曹操","不可能绝对不可能");
map.put("刘备","接着奏乐接着舞");
map.put("柯镇恶","看我眼色行事");
/**
* 3.利用lambda表达式进行遍历
* 底层:
* forEach其实就是利用第二种方式进行遍历,依次得到每一个键和值
* 再调用accept方法
*/
System.out.println("----------匿名内部类方式-----------");
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key + "=" + value);
}
});
System.out.println("---------Lambda表达式方式--------------");
map.forEach((key, value)-> System.out.println(key + "=" + value));
}
}
HashMap中没有什么新方法,直接使用Map中的方法即可。
(1)需求
需求:创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。
存储三个键值对元素,并遍历
要求:同姓名,同年龄认为是同一个学生
核心点:HashMap的键位置如果存储的是自定义对象,需要重写hashCode和equals方法。
(2)代码实现
学生类
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;
}
// 重写equals方法
@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);
}
// 重写hashCode方法
@Override
public int hashCode() {
//return Objects.hash(name, age);
return name.hashCode();
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
测试类
public class HashMapDemo01 {
public static void main(String[] args) {
//1.创建HashMap的对象
HashMap<Student,String> hm = new HashMap<>();
//2.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("wangwu",25);
//3.添加元素
hm.put(s1,"江苏");
hm.put(s2,"浙江");
hm.put(s3,"福建");
hm.put(s4,"山东");
//4.遍历集合
System.out.println("-------键找值方式-------");
Set<Student> keys = hm.keySet();
for (Student key : keys) {
String value = hm.get(key);
System.out.println(key + "=" + value);
}
System.out.println("---------键值对方式----------");
Set<Map.Entry<Student, String>> entries = hm.entrySet();
for (Map.Entry<Student, String> entry : entries) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
System.out.println("--------BiConsumer(Lambda表达式)方式------------");
hm.forEach((student, s)-> System.out.println(student + "=" + s));
}
}
(1)需求
某个班级80名学生,现在需要组成秋游活动,
班长提供了四个景点依次是(A、B、C、D),
每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
(2)代码实现
public class HashMapDemo02 {
public static void main(String[] args) {
//1.需要先让同学们投票,定义一个数组,存储4个景点
String[] arr = {"A","B","C","D"};
//利用随机数模拟80个同学的投票,并把投票的结果存储起来
ArrayList<String> list = new ArrayList<>();
Random r = new Random(); // 随机数
for (int i = 0; i < 80; i++) {
int index = r.nextInt(arr.length);//使用随机数模拟投票,这里生成了范围在[0,1,2,3]的随机数
list.add(arr[index]);
}
//2.如果要统计的东西比较多,不方便使用计数器思想,可以定义map集合,利用集合进行统计
HashMap<String,Integer> hm = new HashMap<>();
for (String name : list) {
//判断当前的景点在map集合当中是否存在
if(hm.containsKey(name)){
//存在
//先获取当前景点已经被投票的次数
int count = hm.get(name);
//表示当前景点又被投了一次
count++;
//把新的次数再次添加到集合当中
hm.put(name,count);
}else{
//不存在
hm.put(name,1);
}
}
System.out.println("每个景点的统计结果:"+hm);
//3.求最大值
int max = 0;
Set<Map.Entry<String, Integer>> entries = hm.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
int count = entry.getValue();
if(count > max){
max = count;
}
}
System.out.println("景点票数最大值:"+max);
//4.判断哪个景点的次数跟最大值一样,如果一样,打印出来
for (Map.Entry<String, Integer> entry : entries) {
int count = entry.getValue();
if(count == max){
System.out.println("最多人想去的景点是:"+entry.getKey());
}
}
}
}
LinkedHashMap中没有什么新方法,直接使用Map中的方法即可。
使用示例:
public class LinkedHashMapDemo {
public static void main(String[] args) {
//1.创建集合
LinkedHashMap<String,Integer> lhm = new LinkedHashMap<>();
//2.添加元素
lhm.put("c",789);
lhm.put("b",456);
lhm.put("a",123);
lhm.put("a",111);
lhm.put("d",789);
//3.打印集合
System.out.println(lhm);
}
}
(1)TreeMap的键不重复、无索引、可排序;
(2)TreeMap底层是红黑树结构;
(3)默认按照键的从小到大进行排序,也可以自己规定键的排序规则(依赖自然排序Comparable或者比较器排序Comparator),Integer Double默认情况下都是按照升序排列的,String按照字母在ASCII码表中对应的数字升序进行排列;
(4)如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则:
(1)案例需求
(2)代码实现
学生类
public class Student implements Comparable<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;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
// 自定义排序规则
@Override
public int compareTo(Student o) {
//按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人。
//this:表示当前要添加的元素
//o:表示已经在红黑树中存在的元素
//返回值:
//负数:表示当前要添加的元素是小的,存左边
//正数:表示当前要添加的元素是大的,存右边
//0:表示当前要添加的元素已经存在,舍弃
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
测试类
public class TreeMapDemo02 {
public static void main(String[] args) {
//1.创建集合
TreeMap<Student,String> tm = new TreeMap<>();
//2.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("lihua",23);
Student s5 = new Student("wangwu",25);
//3.添加元素
tm.put(s1,"江苏");
tm.put(s2,"天津");
tm.put(s3,"北京");
tm.put(s4,"上海");
tm.put(s5,"北京");
//4.打印集合
System.out.println(tm);
}
}
需求1:
键:整数表示id
值:字符串表示商品名称
要求1:按照id(key)的升序排列 o1 - o2
要求2:按照id(key)的降序排列 o2 - o1
public class TreeMapDemo01 {
public static void main(String[] args) {
/**
* 1.创建集合对象
* Integer Double 默认情况下都是按照升序排列的
* String 按照字母再ASCII码表中对应的数字升序进行排列
*/
TreeMap<Integer,String> tm = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//o1:当前要添加的元素
//o2:表示已经在红黑树中存在的元素
return o2 - o1;
}
});
//2.添加元素
tm.put(2,"康帅傅");
tm.put(5,"可恰可乐");
tm.put(4,"雷碧");
tm.put(3,"九个核桃");
tm.put(1,"粤利粤");
//3.打印集合
System.out.println(tm);
}
}
需求:
字符串“aababcabcdabcde”
请统计字符串中每一个字符出现的次数,并按照以下格式输出
输出结果:a(5)b(4)c(3)d(2)e(1)
统计思想:利用map集合进行统计
键:表示要统计的内容
值:表示次数
如果题目中没有要求对结果进行排序,默认使用HashMap
如果题目中要求对结果进行排序,请使用TreeMap
public class TreeMapDemo03 {
public static void main(String[] args) {
//1.定义字符串
String s = "aababcabcdabcde";
//2.创建集合
TreeMap<Character,Integer> tm = new TreeMap<>();
//3.遍历字符串得到里面的每一个字符
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//拿着c到集合中判断是否存在
//存在,表示当前字符又出现了一次
//不存在,表示当前字符是第一次出现
if(tm.containsKey(c)){
//存在
//先把已经出现的次数拿出来
int count = tm.get(c);
//当前字符又出现了一次
count++;
//把自增之后的结果再添加到集合当中
tm.put(c,count);
}else{
//不存在
tm.put(c,1);
}
}
//4.遍历集合,并按照指定的格式进行拼接
// a(5)b(4)c(3)d(2)e(1)
System.out.println("--------使用StringBuilder进行拼接-----------");
StringBuilder sb = new StringBuilder();
// 遍历集合并进行拼接
tm.forEach((key, value)->sb.append(key).append("(").append(value).append(")"));
System.out.println(sb);
System.out.println("--------使用StringJoiner进行拼接-----------");
StringJoiner sj = new StringJoiner("","","");
// 遍历集合并进行拼接
tm.forEach((key, value)->sj.add(key + "").add("(").add(value + "").add(")"));
System.out.println(sj);
}
}
(1)TreeMap添加元素的时候,键是否需要重写hashCode和equals方法?
此时是不需要重写的。
(2)HashMap是哈希表结构的,JDK8开始由数组,链表,红黑树组成的。既然有红黑树,HashMap的键是否需要实现Compareable接口或者传递比较器对象呢?
不需要的。因为在HashMap的底层,默认是利用哈希值的大小关系来创建红黑树的。
(3)TreeMap和HashMap谁的效率更高?
如果是最坏情况,添加了8个元素,这8个元素形成了链表,此时TreeMap的效率要更高。但是这种情况出现的几率非常的少。一般而言,还是HashMap的效率要更高。
(4)你觉得在Map集合中,java会提供一个如果键重复了,不会覆盖的put方法呢?
此时putIfAbsent本身不重要。
传递一个思想:代码中的逻辑都有两面性,如果我们只知道了其中的A面,而且代码中还发现了有变量可以控制两面性的发生。那么该逻辑一定会有B面。
习惯:
(5)HashMap、LinkedHashMap、TreeMap三种双列集合,以后如何选择?
默认:HashMap(效率最高);如果要保证存取有序:LinkedHashMap;如果要进行排序:TreeMap
有关Properties集合与IO流的操作,查看IO流部分的笔记。
Properties
是 Java 中用于处理配置信息的集合类。它继承自 Hashtable
类,因此具备了存储键值对的特性。Properties
主要用于处理属性文件,其中包含了应用程序的配置信息,例如数据库连接信息、用户界面设置等。Properties
继承自 Hashtable
,因此也具备了 Hashtable
的一些方法。
(1)键值对存储: Properties
以键值对的形式存储数据,其中键和值都是字符串类型。每个键和值都是唯一的,且键和值都不能为 null
。
(2)读取和写入属性文件: Properties
提供了 load
和 store
方法,用于从输入流读取属性文件内容,以及将属性保存到输出流中。这使得它非常适合处理配置文件。
(3)获取和设置属性值: 使用 getProperty
方法可以通过键获取属性值。通过 setProperty
方法可以设置键值对。
(4)默认值: 如果在使用 getProperty
方法时指定了默认值,当键不存在时将返回默认值。
(5)列出所有属性: 使用 stringPropertyNames
方法可以获取所有键的集合。
(1)Properties()
: 默认构造方法,创建一个空的 Properties
对象。
Properties prop = new Properties();
(2)Properties(Properties defaults)
: 使用指定的默认属性创建一个 Properties
对象。
Properties defaultProps = new Properties();
defaultProps.setProperty("defaultKey", "defaultValue");
Properties prop = new Properties(defaultProps);
(1)setProperty(String key, String value)
: 设置指定键的值。
prop.setProperty("database.url", "jdbc:mysql://localhost:3306/mydatabase");
(2)getProperty(String key)
: 获取指定键的值。
String value = prop.getProperty("database.url");
(3)getProperty(String key, String defaultValue)
: 获取指定键的值,如果键不存在则返回默认值。
String value = prop.getProperty("database.url", "defaultUrl");
(4)load(InputStream input)
: 从输入流中加载属性文件。
try (InputStream input = new FileInputStream("config.properties")) {
prop.load(input);
} catch (IOException e) {
e.printStackTrace();
}
(5)store(OutputStream output, String comments)
: 将属性存储到输出流中,可以附带注释。
try (OutputStream output = new FileOutputStream("config.properties")) {
prop.store(output, "This is a comment");
} catch (IOException e) {
e.printStackTrace();
}
(6)stringPropertyNames()
: 返回所有键的集合。
Set<String> keys = prop.stringPropertyNames();
public class PropertiesDemo {
public static void main(String[] args) {
//1.创建集合的对象
Properties prop = new Properties();
//2.添加数据
//细节:虽然可以往Properties当中添加任意的数据类型,但是一般只会往里面添加字符串类型的数据
prop.put("aaa","111");
prop.put("bbb","222");
prop.put("ccc","333");
prop.put("ddd","444");
//3.遍历集合
System.out.println("---------键找值方式遍历-------------");
Set<Object> keys = prop.keySet();
for (Object key : keys) {
Object value = prop.get(key);
System.out.println(key + "=" + value);
}
System.out.println("---------键值对对象方式遍历-------------");
Set<Map.Entry<Object, Object>> entries = prop.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + "=" + value);
}
}
}
Collections不是集合,而是工具类。
java.utils.Collections
是集合工具类,用来对集合进行操作。常用方法如下:
public static boolean addAll(Collection c, T...elements)
:批量添加元素(单列集合)public static void shuffle(List> list)
:打乱集合顺序。public static void sort(List list)
:将集合中元素按照默认规则排序。public static void sort(List list,Comparator super T> )
:将集合中元素按照指定规则排序。代码演示:
public class MyCollectionsDemo01 {
public static void main(String[] args) {
//addAll 批量添加元素
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.批量添加元素
Collections.addAll(list,"abc","bcd","qwer","df","asdf","zxcv","1234","qwer");
//3.打印集合
System.out.println(list);
//shuffle 打乱
Collections.shuffle(list);
System.out.println(list);
}
}
public class MyCollectionsDemo02 {
public static void main(String[] args) {
System.out.println("-------------1、sort默认规则--------------------------");
//默认规则,需要重写Comparable接口compareTo方法。Integer已经实现,按照从小打大的顺序排列
//如果是自定义对象,需要自己指定规则
ArrayList<Integer> list1 = new ArrayList<>();
Collections.addAll(list1, 10, 1, 2, 4, 8, 5, 9, 6, 7, 3);
Collections.sort(list1);
System.out.println(list1);
System.out.println("-------------2、sort自己指定规则规则--------------------------");
Collections.sort(list1, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list1);
Collections.sort(list1, (o1, o2) -> o2 - o1);
System.out.println(list1);
System.out.println("-------------3、binarySearch--------------------------");
//需要元素有序
ArrayList<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(Collections.binarySearch(list2, 9));
System.out.println(Collections.binarySearch(list2, 1));
System.out.println(Collections.binarySearch(list2, 20));
System.out.println("-------------4、copy--------------------------");
//把list3中的元素拷贝到list4中
//会覆盖原来的元素
//注意点:如果list3的长度 > list4的长度,方法会报错
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Integer> list4 = new ArrayList<>();
Collections.addAll(list3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Collections.addAll(list4, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
Collections.copy(list4, list3);
System.out.println(list3);
System.out.println(list4);
System.out.println("-------------5、fill--------------------------");
//把集合中现有的所有数据,都修改为指定数据
ArrayList<Integer> list5 = new ArrayList<>();
Collections.addAll(list5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Collections.fill(list5, 100);
System.out.println(list5);
System.out.println("-------------6、max/min--------------------------");
//求最大值或者最小值
ArrayList<Integer> list6 = new ArrayList<>();
Collections.addAll(list6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(Collections.max(list6));
System.out.println(Collections.min(list6));
System.out.println("-------------7、max/min指定规则--------------------------");
// String中默认是按照字母的abcdefg顺序进行排列的
// 现在我要求最长的字符串
// 默认的规则无法满足,可以自己指定规则
// 求指定规则的最大值或者最小值
ArrayList<String> list7 = new ArrayList<>();
Collections.addAll(list7, "a","aa","aaa","aaaa");
System.out.println(Collections.max(list7, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
}));
System.out.println("-------------8、swap--------------------------");
ArrayList<Integer> list8 = new ArrayList<>();
Collections.addAll(list8, 1, 2, 3);
Collections.swap(list8,0,2);
System.out.println(list8);
}
}