文章目录
- 1、集合体系结构
- 2、单列集合
- 2.1 Collection
- 2.1.1 Collection集合概述和使用
- 2.1.2 自定义对象比较
- 2.1.3 Collection集合的遍历
- 2.1.3.1 迭代器遍历
- 2.1.3.2 增强for
- 2.1.3.3 lambda表达式
- 2.2 List
- 2.2.1 List集合的概述和特点
- 2.2.2 List集合的特有方法
- 2.2.3 List集合的两个删除方法
- 2.2.4 List集合的五种遍历方式
- 2.3 ArrayList
- 2.3.1 ArrayList集合概述
- 2.3.2 常用方法
- 2.3.3 存储字符串并遍历
- 2.3.4 存储学生对象并遍历
- 2.3.5 查找用户的索引
- 2.3.6 判断用户id是否存在
- 2.4 LinkedList
- 2.5 Set
- 2.5.1 Set集合概述和特点
- 2.5.2 Set集合的遍历
- 2.6 HashSet
- 2.6.1 哈希值
- 2.6.2 比较对象内部属性值
- 2.7 LinkedHashSet
- 2.8 TreeSet
- 2.8.1 TreeSet集合概述和特点
- 2.8.2 存储整数并进行遍历
- 2.8.3 自然排序Comparable的使用
- 2.8.4 比较器排序Comparator的使用
- 2.8.5 两种比较方式总结
- 3、双列集合
- 3.1 Map
- 3.1.1 Map集合概述和特点
- 3.1.2 Map集合的基本功能
- 3.1.3 Map集合的获取功能
- 3.1.4 键找值的方式遍历
- 3.1.5 键值对方式遍历
- 3.1.6 BiConsumer(Lambda表达式)方式遍历
- 3.2 HashMap
- 3.2.1 HashMap集合概述和特点
- 3.2.2 HashMap集合练习
- 3.2.2.1 练习1
- 3.2.2.1 练习2
- 3.3 LinkedHashMap
- 3.4 TreeMap
- 3.4.1 TreeMap集合概述和特点
- 3.4.2 TreeMap自定义排序规则
- 3.4.2.1 Comparable
- 3.4.2.2 Comparator
- 3.4.3 TreeMap集合遍历
- 3.4.4 Map集合问答
- 3.5 Properties
- 3.5.1 Properties集合概述
- 3.5.2 常用构造方法
- 3.5.3 常用成员方法
- 3.5.4 Properties集合遍历
- 4、Collections
1、集合体系结构
(1)Collection接口
- List接口: 有序集合,允许重复元素。常见的实现类有ArrayList、LinkedList、Vector。
- Set接口: 无序集合,不允许重复元素。常见的实现类有HashSet、LinkedHashSet、TreeSet。
- Queue接口:先进先出的集合,常见实现类有LinkedList和PriorityQueue
- Deque接口:ArrayDeque类可变大小的双端队列,数组实现。
(2)Map接口
- HashMap类: 基于哈希表实现的键值对映射,不保证顺序。
- LinkedHashMap类: 基于哈希表和双向链表实现,保持插入顺序。
- TreeMap类: 基于红黑树实现,按键的自然顺序或自定义顺序排序。
(3)其他实用类:
- LinkedList:实现了List和Queue接口,支持高效的插入和删除操作
- ArrayList:实现了List接口,使用数组存储,支持快速随机访问,但插入删除效率较低
- HashSet:实现了Set接口,使用哈希表存储,无序且不允许重复元素
- TreeSet:实现了Set和SortedSet接口,使用二叉搜索树存储,有序且不可重复
- HashMap:实现了Map接口,使用键值对存储,使用哈希表实现,相对快速
- LinkedHashMap:继承HashMap,使用链表维护元素次序
- Properties:用来处理键值对配置文件
- EnumSet:用于枚举类型的set实现
- Collections工具类:提供对集合常用操作的静态方法如排序、搜索、同步包装等
(4)数组和集合的区别
- 相同点:都是容器,可以存储多个数据
- 不同点
- 数组的长度是不可变的,集合的长度是可变的
- 数组可以存基本数据类型和引用数据类型;集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类。
(5)单列集合:Collection
(6)双列集合:Map
2、单列集合
2.1 Collection
2.1.1 Collection集合概述和使用
(1)Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
(2)创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
(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}
}
2.1.2 自定义对象比较
需求:如果同姓名和同年龄,就认为是同一个学生。
因为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的快捷键快速实现)@Overridepublic 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));}
}
2.1.3 Collection集合的遍历
2.1.3.1 迭代器遍历
迭代器不依赖下标。
Java中为什么要有迭代器Iterator?
- 统一访问方式: 迭代器提供了一种一致的访问方式,无论集合的具体实现是什么,都可以通过相同的接口方法来访问元素,这样简化了代码并提高了代码的可读性。
- 隐藏集合内部实现: 迭代器使得客户端代码无需了解集合内部的实现细节,只需知道如何使用迭代器的方法,从而提高了代码的封装性。如果集合的实现发生变化,只需调整迭代器的实现而不需要修改客户端代码。
- 支持并发访问: 在多线程环境中,使用迭代器遍历集合是一种安全的方式,因为它提供了一种在迭代过程中防止其他线程修改集合的机制。一些集合类的迭代器实现,如
ArrayList
的Iterator
,不支持在迭代过程中修改集合,而在迭代器创建时会记录集合的修改次数,如果在迭代过程中修改了集合,就会抛出ConcurrentModificationException
异常。- 降低耦合度: 使用迭代器可以将集合的遍历逻辑和具体的集合实现分离开来,这样可以降低代码的耦合度。客户端代码只需要通过迭代器访问元素,而不需要关心集合的内部结构。
(1)迭代器介绍
- 迭代器,集合的专用遍历方式
- Iterator iterator():返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
(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.报错NoSuchElementException2.迭代器遍历完毕,指针不会复位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"); // 报错ConcurrentModificationExceptionit.remove();}}System.out.println(coll);}
}
2.1.3.2 增强for
(1)介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
- 所有的单列集合和数组才能用增强fo进行遍历
- 修改增强for中的变量str,不会改变集合中原本的数据
(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}
}
2.1.3.3 lambda表达式
可以先写成匿名内部类的方式再改为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>() {@Overridepublic void accept(String s) {System.out.println(s);}});System.out.println("---------lambda表达式方式---------");// lambda表达式coll.forEach(s -> System.out.println(s));}
}
总结:
迭代器:在遍历的过程中需要删除元素,请使用迭代器。
增强for、Lambda:仅仅想遍历,那么使用增强for或Lambda表达式。
2.2 List
2.2.1 List集合的概述和特点
(1)List集合的概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
(2)List集合的特点
- 存取有序
- 可以重复
- 有索引
2.2.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");//1list.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); // bbbSystem.out.println(list);//6、返回指定索引处的元素String s = list.get(2);System.out.println(s); // ccc}
}
2.2.3 List集合的两个删除方法
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);}
}
2.2.4 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>() {@Overridepublic 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。
2.3 ArrayList
集合和数组的优势对比:
- 集合长度可变
- 集合添加数据的时候不需要考虑索引,默认将数据添加到末尾
2.3.1 ArrayList集合概述
-
什么是集合:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
-
ArrayList集合的特点:长度可以变化,只能存储引用数据类型。
-
泛型的使用:用于约束集合中存储元素的数据类型
2.3.2 常用方法
构造方法
方法名 | 说明 |
---|---|
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);}}
}
2.3.3 存储字符串并遍历
案例需求:创建一个存储字符串的集合,存储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("]");}
}
2.3.4 存储学生对象并遍历
案例需求:创建一个存储学生对象的集合,存储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());}}
}
2.3.5 查找用户的索引
需求:
- main方法中定义一个集合,存入三个用户对象。 用户属性为:id,username,password
- 要求:定义一个方法,根据id查找对应的学生信息。
- 如果存在,返回索引
- 如果不存在,返回-1
代码示例:
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;}
}
2.3.6 判断用户id是否存在
需求:
- main方法中定义一个集合,存入三个用户对象。用户属性为:id,username,password
- 要求:定义一个方法,根据id查找对应的学生信息。
- 如果存在,返回true
- 如果不存在,返回false
代码实现:
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);}//定义在测试类中的方法需要加staticpublic 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;}
}
2.4 LinkedList
数组结构
查询快、增删慢
队列结构
查询慢、增删快
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);}
}
2.5 Set
2.5.1 Set集合概述和特点
- 无序:存取顺序不一致
- 不重复:不可以存储重复元素
- 无索引:没有索引,不能使用普通for循环遍历
Set集合的实现类:
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:可排序、不重复、无索引
Set接口中的方法上基本上与Collection的API一致。
2.5.2 Set集合的遍历
利用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>() {@Overridepublic void accept(String str) {System.out.println(str);}});System.out.println("-------Lambda表达式方式遍历---------");s.forEach( str->System.out.println(str));}
}
2.6 HashSet
没有什么新方法,使用Collection中的方法即可。
重写hashcode:想要根据属性值计算hash值。
重写equals:想要在比较时,比的是对象内部的属性值。
2.6.1 哈希值
哈希值:对象的整数表现形式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());//1324119927System.out.println(s2.hashCode());//990368553//在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。//哈希碰撞System.out.println("abc".hashCode());//96354System.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
2.6.2 比较对象内部属性值
在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);}
}
2.7 LinkedHashSet
没有什么新方法,使用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);}
}
2.8 TreeSet
没有什么新方法,使用Collection中的方法即可。TreeSet会默认对元素进行升序排序,底层基于红黑树实现排序。
对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序
对于字符、字符串类型:按照字符在ASC码表中的数字升序进行排序
对于自定义的对象、需要自定义排序的类型有两种方法:
- 方式一:可以使用Javabean类实现Comparable接口
- 方式二:创建集合时,自定义Comparator比较器对象,自定义排序的方法
- 如果方式一和方式二同时存在,以方式二定义的规则为准
2.8.1 TreeSet集合概述和特点
- 不可以存储重复元素
- 没有索引
- 可以将元素按照规则进行排序
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator) :根据指定的比较器进行排序
2.8.2 存储整数并进行遍历
存储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增强forfor (int t : ts) {System.out.println(t);}//4.3 lambdaSystem.out.println("--------匿名内部类-------");ts.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});System.out.println("--------lambda-------");ts.forEach( i-> System.out.println(i));}
}
2.8.3 自然排序Comparable的使用
(1)案例需求
- 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
(2)实现步骤
- 使用空参构造创建TreeSet集合。用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自定义的Student类实现Comparable接口。自然排序,就是让元素所属的类实现Comparable接口,重写**compareTo(T o)**方法
- 重写接口中的compareTo方法。重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
(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 :表示当前要添加的元素已经存在,舍弃*/@Overridepublic 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);}
}
2.8.4 比较器排序Comparator的使用
(1)案例需求
需求:请自行选择比较器排序和自然排序两种方式;
要求:存入四个字符串, “df”,“c”, “qwer”, “ab”
按照长度排序,如果一样长则按照首字母排序(如果不自定义排序规则,字符串默认根据ASCII排序)
采取第二种排序方式:比较器排序
(2)实现步骤
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写**compare(T o1,T o2)**方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
(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>() {@Overridepublic 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>() {@Overridepublic 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()));}
}
2.8.5 两种比较方式总结
(1)两种比较方式小结
- 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
- 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
- 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
(2)两种方式中关于返回值的规则
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
(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);}}
}
3、双列集合
键值对、键值对对象:Entry对象。
3.1 Map
3.1.1 Map集合概述和特点
(1)Map集合概述
interface Map<K,V> K:键的类型;V:值的类型
(2)Map集合的特点
- 双列集合,一个键对应一个值
- 键不可以重复,值可以重复
3.1.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);}
}
3.1.3 Map集合的获取功能
(1)方法介绍
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
(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<K> keySet():获取所有键的集合Set<String> keySet = map.keySet();for(String key : keySet) {System.out.println(key);}System.out.println("----------------------------");//Collection<V> values():获取所有值的集合Collection<String> values = map.values();for(String value : values) {System.out.println(value);}}
}
3.1.4 键找值的方式遍历
通过键去找值。
(1)步骤分析
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取到每一个键。可以使用增强for、迭代器、Lambda表达式实现
- 根据键去找值。用get(Object key)方法实现
(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>() {@Overridepublic 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)));}
}
3.1.5 键值对方式遍历
(1)步骤分析
- 获取所有键值对对象的集合
- Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
- 遍历键值对对象的集合,得到每一个键值对对象
- 用增强for实现(也可以使用迭代器或者Lambda表达式),得到每一个Map.Entry
- 根据键值对对象获取键和值
- 用getKey()得到键
- 用getValue()得到值
(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>>() {@Overridepublic 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()));}
}
3.1.6 BiConsumer(Lambda表达式)方式遍历
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>() {@Overridepublic void accept(String key, String value) {System.out.println(key + "=" + value);}});System.out.println("---------Lambda表达式方式--------------");map.forEach((key, value)-> System.out.println(key + "=" + value));}
}
3.2 HashMap
HashMap中没有什么新方法,直接使用Map中的方法即可。
3.2.1 HashMap集合概述和特点
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法
- HashMap是Map里面的一个实现类,没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- 特点都是由键决定的:无序、不重复、无索引
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
- 如果键存储的是自定义对象,需要重写hashCode和equals方法;如果值存储自定义对象,不需要重写hashCode和equals方法
3.2.2 HashMap集合练习
3.2.2.1 练习1
(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方法@Overridepublic 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方法@Overridepublic 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));}
}
3.2.2.1 练习2
(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());}}}
}
3.3 LinkedHashMap
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);}
}
3.4 TreeMap
3.4.1 TreeMap集合概述和特点
(1)TreeMap的键不重复、无索引、可排序;
(2)TreeMap底层是红黑树结构;
(3)默认按照键的从小到大进行排序,也可以自己规定键的排序规则(依赖自然排序Comparable或者比较器排序Comparator),Integer Double默认情况下都是按照升序排列的,String按照字母在ASCII码表中对应的数字升序进行排列;
(4)如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则:
- 实现Comparable接口,指定比较规则。
- 创建集合时传递Comparator比较器对象,指定比较规则。
- Comparable和Comparator都写了比较规则的话,以Comparator为准。
3.4.2 TreeMap自定义排序规则
3.4.2.1 Comparable
(1)案例需求
- 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
- 按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人
(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 + "}";}// 自定义排序规则@Overridepublic 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);}
}
3.4.2.2 Comparator
需求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>() {@Overridepublic 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);}
}
3.4.3 TreeMap集合遍历
需求:
字符串“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);}
}
3.4.4 Map集合问答
(1)TreeMap添加元素的时候,键是否需要重写hashCode和equals方法?
此时是不需要重写的。
(2)HashMap是哈希表结构的,JDK8开始由数组,链表,红黑树组成的。既然有红黑树,HashMap的键是否需要实现Compareable接口或者传递比较器对象呢?
不需要的。因为在HashMap的底层,默认是利用哈希值的大小关系来创建红黑树的。
(3)TreeMap和HashMap谁的效率更高?
如果是最坏情况,添加了8个元素,这8个元素形成了链表,此时TreeMap的效率要更高。但是这种情况出现的几率非常的少。一般而言,还是HashMap的效率要更高。
(4)你觉得在Map集合中,java会提供一个如果键重复了,不会覆盖的put方法呢?
此时putIfAbsent本身不重要。
传递一个思想:代码中的逻辑都有两面性,如果我们只知道了其中的A面,而且代码中还发现了有变量可以控制两面性的发生。那么该逻辑一定会有B面。
习惯:
- boolean类型的变量控制,一般只有AB两面,因为boolean只有两个值
- int类型的变量控制,一般至少有三面,因为int可以取多个值。
(5)HashMap、LinkedHashMap、TreeMap三种双列集合,以后如何选择?
默认:HashMap(效率最高);如果要保证存取有序:LinkedHashMap;如果要进行排序:TreeMap
3.5 Properties
有关Properties集合与IO流的操作,查看IO流部分的笔记。
3.5.1 Properties集合概述
Properties
是 Java 中用于处理配置信息的集合类。它继承自 Hashtable
类,因此具备了存储键值对的特性。Properties
主要用于处理属性文件,其中包含了应用程序的配置信息,例如数据库连接信息、用户界面设置等。Properties
继承自 Hashtable
,因此也具备了 Hashtable
的一些方法。
(1)键值对存储: Properties
以键值对的形式存储数据,其中键和值都是字符串类型。每个键和值都是唯一的,且键和值都不能为 null
。
(2)读取和写入属性文件: Properties
提供了 load
和 store
方法,用于从输入流读取属性文件内容,以及将属性保存到输出流中。这使得它非常适合处理配置文件。
(3)获取和设置属性值: 使用 getProperty
方法可以通过键获取属性值。通过 setProperty
方法可以设置键值对。
(4)默认值: 如果在使用 getProperty
方法时指定了默认值,当键不存在时将返回默认值。
(5)列出所有属性: 使用 stringPropertyNames
方法可以获取所有键的集合。
3.5.2 常用构造方法
(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);
3.5.3 常用成员方法
(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();
3.5.4 Properties集合遍历
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);}}
}
4、Collections
Collections不是集合,而是工具类。
java.utils.Collections
是集合工具类,用来对集合进行操作。常用方法如下:
public static <T> boolean addAll(Collection<T> c, T...elements)
:批量添加元素(单列集合)public static void shuffle(List<?> list)
:打乱集合顺序。public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。public static <T> void sort(List<T> 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>() {@Overridepublic 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>() {@Overridepublic 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);}
}