集合的使用
前提:栈、堆、二叉树、hashcode、toString()、quesalus()的知识深入和底层理解。
1、什么是集合
集合就是咋们所说的容器
前面我们学习过数组 数组也是容器
容器:装东西的 生活中有多少的容器呀? 水杯 教室 酒瓶 水库 只要是能装东西的 都可以看成是容器
我们这个集合的容器 是用来装啥的呢? 装数据?
数据? 一切可以被计算机识别的 文字 图片 视频 音频都是数据
说白了 我们今天所学习的这个集合 就跟前面的数组类似 只是底层的实现不一样而已
2、集合的分类
主要根据我们的值的个数 可以分成 单例集合 和 双列集合
单例集合:简单的说 在这个容器中存放的的值 就是一个一个的 value
单列集合的爹:Collection
双列集合:他在容器中存储的数据就是 键值对 key---value
双列集合的爹:Map
3、单列集合
3.1、List集合
3.1.1、ArrayList
ArrayList<E> 这个集合中的泛型:表示的意思其实就是对这个容器中能存放数据类型的一种约束 比如我们的泛型的数据类型是:User 那么这个容器中只能存放User类型的数据
这个ArrayList底层就是数组 他是有序的 地址空间是连续的 地址空间连续 就意味着能通过开始地址+偏移量来为访问 而且能重复添加数据
有序:一定能通过下标访问数据
List中所有的内容都是可以重复的....
3.1.1.1、集合的使用
public class ArrayListTest {
public static void main(String[] args) {
//第一种方式
List<String> list = new ArrayList<>();
//接下来就可以向这个集合中存放数据了...
list.add("123");
list.add("456");
list.add("789");
list.add("789");
List<String> list2 = new ArrayList<>();
//接下来就可以向这个集合中存放数据了...
list.add("12322");
list.add("45622");
//将list2集合中的数据 添加到 list中来
list.addAll(list2);
//能不能删除数据呢?
//这个就是直接删除某一个元素
list.remove("789");
//还可以通过下标删除
list.remove(0);
//接下来玩下修改呢?
list.set(0,"小波波");
//获取某一个位置的元素
String s = list.get(0);
//判断这个集合中是否存在某一个元素
boolean contains = list.contains("789");
//判断集合是否为空
boolean empty = list.isEmpty();
//返货某一个元素的下标位置
int i = list.indexOf("789");
//截取集合中指定位置的元素 生成一个新的集合
List<String> stringList = list.subList(0, 5);
System.out.println("list集合的size:"+list.size());
List<User> userList=new ArrayList<>();
userList.add(new User(1,"小小","123"));
userList.add(0,new User(0,"这里是测试","xxx"));
System.out.println("userList集合的size:"+userList.size());
System.out.println("userList中的数据是:"+userList.get(0));
}
}
public class ArrayListTest1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//接下来就可以向这个集合中存放数据了...
list.add("123");
list.add("456");
list.add("789");
list.add("789");
//第一种遍历方式:因为这个ArrayList本身底层是数组(Object类型的数组) 数组地址空间连续 所以我们能通过下标来访问
for (int i = 0; i <list.size() ; i++) {
System.out.println("集合中的值:"+list.get(i));
}
System.out.println("------------------------------");
//第二种遍历方式 通过增强的for循环来玩
for (String val:list){
System.out.println("集合中的值:"+val);
}
System.out.println("------------------------------");
//第三种遍历方式通过JDK8中的stream流来遍历
list.stream().forEach(val->{
System.out.println("集合中的值:"+val);
});
System.out.println("------------------------------");
//第四种遍历方式:迭代器 迭代器的游标问题
/**
* 这种情况下不允许对元素进行修改和删除
*
* 其实不止是这种情况 在遍历的情况下 逻辑上都允许修改和删除的产生
*/
Iterator<String> it = list.iterator();
// it.hasNext():判断下一个节点是否有元素
while (it.hasNext()){
// it.next() :取出当前位置的元素
String val = it.next();
System.out.println("通过迭代器取出来的值:"+val);
}
System.out.println("------------------------------");
//第五种遍历方式
ListIterator<String> it1 = list.listIterator();
while (it1.hasNext()){
String next = it1.next();
System.out.println("通过迭代器取出来的值:"+next);
}
}
}
3.1.2、LinkedList
LinkedList底层是链表
链表中包含 一个一个的链条 每一个链条都包含了两部分
当前节点的值 和 下一个元素的地址
链表中 数据存储的地址空间不连续 所以不能使用偏移量来访问
输出的值是连续的。
public class LinkedListTest01 {
public static void main(String[] args) {
//申明对象
List<String> linkedList1 = new LinkedList<>();
linkedList1.add("001");
linkedList1.add("002");
linkedList1.add("003");
linkedList1.add("004");
for (int i = 0; i < linkedList1.size(); i++) {
String val = linkedList1.get(i);
System.out.println("val:"+val);
}
for (String val:linkedList1){
System.out.println("val:"+val);
}
linkedList1.stream().forEach(val->{
System.out.println("val:"+val);
});
}
}
3.1.3、Vector的使用(不常用)
这个底层也是数组 线程安全的 效率不高
这个集合基本不使用 不也用记住
public class VectorTest {
public static void main(String[] args){
List<String> vector=new Vector<>();
vector.add("中国好");
vector.add("小日子");
for (int i = 0; i <vector.size() ; i++) {
System.out.println("数据是:"+vector.get(i));
}
}
}
3.1.4、Stack(栈)
public class StackTest {
public static void main(String[] args){
//这个其实是栈的数据结构
Stack<String> list=new Stack<String>();
list.push("1");
list.push("2");
list.push("3");
list.push("4");
list.push("5");
System.out.println("pop:"+list.pop());
System.out.println("pop:"+list.pop());
System.out.println("pop:"+list.pop());
System.out.println("pop:"+list.pop());
System.out.println("pop:"+list.pop());
}
}
3.2、Set集合
逻辑上 Set集合逻辑上是无序的 而且Set集合能排重 不能通过下标直接访问
Set<E> 的爹 依然是Collection
Set这个接口是所有Set集合的爹
Set排重的原则是啥?
如果在Set集合中存放的是对象比如User 那么他就首先会去调用这个User中的 hashCode方法 然后获取到当前的这个要添加数据的hashCode值去和已经添加数据的HashCode值 做比较 如果是不等 那么说明肯定不是一个元素 那么直接添加元素 如果是HashCode值 遇到了在已经添加的数据中的HashCode值是相等的话 那么都说明有可能这个值是重复的 如果是这个值是重复的话 那就去调用当前这个对象的equals方法 判断equals方法是不是返回true 如果返回true 那么说明值重复 不添加数据 如果返回的是false 那么说明值 不重复 那么就可以添加数据
3.2.1、Set集合的排重问题(HashSet)
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("123");
set.add("345");
set.add("234");
set.add("345");
System.out.println("数据的个数:" + set.size());
System.out.println("-----------------------------");
Set<User> setUser = new HashSet<>();
setUser.add(new User(1, "小小", "112"));
setUser.add(new User(1, "小小", "134"));
setUser.add(new User(1, "小小", "165"));
setUser.add(new User(1, "小小", "178"));
System.out.println("数据的个数:" + setUser.size());
}
public class User {
private Integer id;
private String username;
private String password;
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public int hashCode() {
return this.username.hashCode();
}
/**
* 用户名一样 那么我们就认为这是同一个数据
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if(obj instanceof User){
User user= (User) obj;
return this.username.equals(user.getUsername());
}
return false;
}
}
3.2.2、Set集合的遍历问题(HashSet)
public class HashSetTest1 {
public static void main(String[] args) {
Set<User> set = new HashSet<>();
set.add(new User(1,"xiaobobo","123"));
set.add(new User(-1,"tiedan","777"));
set.add(new User(3,"gousheng","0"));
set.add(new User(4,"gouwa","234"));
set.add(new User(1,"ergouzi","234"));
//通过增强的for循环能访问
for (User val:set){
System.out.println("数据:"+val);
}
System.out.println("--------------------------");
Iterator<User> it = set.iterator();
while (it.hasNext()){
User next = it.next();
System.out.println("拿到的数据是:"+next);
}
System.out.println("--------------");
set.stream().forEach(user -> {
System.out.println("读取到的数据是:"+user);
});
}
}
3.2.3、LinkedHashSet的使用
这个集合的底层是链表
这个集合的数据保存是有序的
public class LinkedHashSetTest {
public static void main(String[] args) {
Set<User> set = new LinkedHashSet<>();
set.add(new User(1, "xiaobobo", "123"));
set.add(new User(-1, "tiedan", "777"));
set.add(new User(3, "gousheng", "0"));
set.add(new User(4, "gouwa", "234"));
set.add(new User(1, "gouwa", "234"));
Iterator<User> it = set.iterator();
while (it.hasNext()) {
User next = it.next();
System.out.println(next);
}
}
}
3.2.4、TreeSet(底层实现是红黑树)
TreeSet和其他的Set集合一样 具有 排重的特性
TreeSet的底层是红黑树--->二叉树--->数据结构--->有大小关系
TreeSet集合自动具有排序的功能
这个排序 就涉及到一个大小的问题
自然数的大小
字符串如何比较大小呢? unicode编码值
3.2.5、两个字符串如何比较大小呢?通过编码
// 在String这个类中为我们提供了这个比较两个字符串大小的方法
public static void main(String[] args) {
String str = "Ab";
String str2 = "Ab";
/**
* 返回值 0:表示的是前后相等
* 返回值-1:表示的是前面小于后面
* 返回值是1:表示的是前面大于后面
*/
System.out.println(str2.compareTo(str));
}
3.2.6、TreeSet的使用
TreeSet的底层使用的是红黑树来实现的
3.2.6.1、TreeSet的基本使用
package com.qfedu.edu.collection.set;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
set.add(123);
set.add(0);
set.add(345);
set.add(77);
set.add(77);
set.add(89);
//遍历这些数据
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
Integer next = it.next();
System.out.println("数据是:" + next);
}
//--------------下面研究字符串的排序------------
System.out.println("-------------------");
Set<String> setStr = new TreeSet<>();
setStr.add("Abc");
setStr.add("Bc");
setStr.add("Ac");
//遍历这些数据
Iterator<String> it1 = setStr.iterator();
while (it1.hasNext()) {
String next = it1.next();
System.out.println("数据是:" + next);
}
}
}
3.2.6.2、Comparable接口实现对象的比较
1、编写Employee对象
public class Employee implements Comparable<Employee>{
private Integer id;
private String name;
private Integer salary;
private String address;
public Employee(Integer id, String name, Integer salary, String address) {
this.id = id;
this.name = name;
this.salary = salary;
this.address = address;
}
public Employee() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
/**
* 比较的方法
* 你需要按照谁排序 那么下面你就按照什么来比较
*
* 我要通过薪资排序
* 薪资是int类型 那么下面就直接做减法
* @param o the object to be compared.
* @return
*/
// @Override
// public int compareTo(Employee o) {
// return this.salary-o.getSalary();
// }
/**
* 下面演示通过姓名来排序
* 姓名是字符串类型
* @param o the object to be compared.
* @return
*/
/* @Override
public int compareTo(Employee o) {
return this.getName().compareTo(o.getName());
}*/
/**
* 如果薪资不为空 那么按照薪资排序
* 如果薪资为空 并且姓名不为空 那么按照姓名排序
* 如果姓名为空 那么就按照id排序....
* @param o the object to be compared.
* @return
*/
@Override
public int compareTo(Employee o) {
if(o.getSalary()!=null){
return this.getSalary()-o.getSalary();
}else if(o.getName()!=null&&!("".equals(o.getName()))){
return this.getName().compareTo(o.getName());
}else{
return this.id-o.getId();
}
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", address='" + address + '\'' +
'}';
}
}
2、编写测试类
public class TreeSetTest1 {
public static void main(String[] args) {
Set<Employee> set = new TreeSet<>();
set.add(new Employee(1,"xiaobobo",3500,"四川成都"));
set.add(new Employee(2,"xiaowangzi",1800,"四川巴中"));
set.add(new Employee(3,"tiedan",2600,"四川自贡"));
set.add(new Employee(4,"gousheng",15000,"四川绵阳"));
set.add(new Employee(5,"gouwa",2700,"四川德阳"));
Iterator<Employee> iterator = set.iterator();
while (iterator.hasNext()){
Employee next = iterator.next();
System.out.println("获取到的数据是:"+next);
}
}
}
3.2.6.3、使用Comparator来实现对象的排序
1、对象的编写
public class Employee1{
private Integer id;
private String name;
private Integer salary;
private String address;
public Employee1(Integer id, String name, Integer salary, String address) {
this.id = id;
this.name = name;
this.salary = salary;
this.address = address;
}
public Employee1() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", address='" + address + '\'' +
'}';
}
}
2、测试的编写
package com.qfedu.edu.collection.set;
import com.qfedu.edu.pojo.Employee;
import com.qfedu.edu.pojo.Employee1;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* @author xiaobobo
* @title: TreeSetTest
* @projectName CD-Java-JY-2401-Simple-Parent
* @description: 这个研究下TreeSet的对象的排序问题
* @date 2024/3/19 15:26
*/
public class TreeSetTest2 {
public static void main(String[] args) {
Set<Employee1> set = new TreeSet<>(new MyComparator());
set.add(new Employee1(1,"xiaobobo",3500,"四川成都"));
set.add(new Employee1(2,"xiaowangzi",1800,"四川巴中"));
set.add(new Employee1(3,"tiedan",2600,"四川自贡"));
set.add(new Employee1(4,"gousheng",15000,"四川绵阳"));
set.add(new Employee1(5,"gouwa",2700,"四川德阳"));
Iterator<Employee1> iterator = set.iterator();
while (iterator.hasNext()){
Employee1 next = iterator.next();
System.out.println("获取到的数据是:"+next);
}
}
/**
* 自定义了一个比较器 这个跟上一个接口是一样的
*/
static class MyComparator implements Comparator<Employee1>{
/**
*
* @param o1 新数据
* @param o2 老数据
* 按照薪资排序 做减法 按照谁排序 就用谁来做比较
* @return
*/
@Override
public int compare(Employee1 o1, Employee1 o2) {
return o1.getSalary()-o2.getSalary();
}
}
}