1 HashSet集合
1.1 HashSet集合概述和特点
- 底层数据结构是哈希表
- 不能保证存储和取出的顺序完全一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以元素唯一
1.2 HashSet集合的基本应用
import java.util.HashSet;
import java.util.Iterator;
/**
* 添加字符串并进行遍历
*/
public class HashSetDemo1 {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<>();
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("java");
hs.add("java");
hs.add("java");
hs.add("java");
hs.add("java");
Iterator<String> it = hs.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("=============================");
for (String s : hs) {
System.out.println(s);
}
}
}
1.3 哈希值
- 哈希值(哈希码值)
- 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
- 如何获取哈希值
- Object类中的public int hashCode():返回对象的哈希码值
- 哈希值的特点
-
如果没有重写hashCode()方法,那么是根据对象的地址值计算出的哈希值
同一个对象多次调用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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
//我们可以对Object类中的hashCode方法进行重写
//在重写之后,就一般是根据对象的属性值来计算哈希值的。
//此时跟对象的地址值就没有任何关系了。
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/**
* 计算哈希值
*/
public class HashSetDemo2 {
public static void main(String[] args) {
Student s1 = new Student("xiaozhi", 23);
Student s2 = new Student("xiaomei", 22);
//因为在Object类中,是根据对象的地址值计算出来的哈希值。
System.out.println(s1.hashCode());//265724891
System.out.println(s1.hashCode());//265724891
System.out.println(s2.hashCode());//265334724
}
}
1.4 常见数据结构之哈希表
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- JDK8以后,在长度比较长的时候,底层实现了优化
- 节点个数少于等于8个
- 数组 + 链表
- 节点个数多于8个
- 数组 + 红黑树
- 节点个数少于等于8个
总结
- 创建一个默认长度16,默认加载因为0.75的数组
- 根据元素的哈希值跟数组的长度计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入
- 如果位置不为null,表示有元素,则调用equals方法比较属性值
- 如果一样,则不存,如果不一样,则存入数组,老元素挂在新元素下面
- 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
- 利用HashSet存储自定义元素,必须重写hashCode和equals方法
1.5 案例 HashSet集合存储学生对象并遍历
- 案例需求
- 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
- 要求:学生对象的成员变量值相同,我们就认为是同一个对象
- 思路:
- 定义学生类
- 创建HashSet集合对象
- 创建学生对象
- 把学生添加到集合
- 遍历集合(增强for)
- 在学生类中重写两个方法
- hashCode()和equals()
- 自动生成即可
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
import java.util.HashSet;
/**
* 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
* 要求:学生对象的成员变量值相同,我们就认为是同一个对象
* 结论:
* 如果HashSet集合要存储自定义对象,那么必须重写hashCode和equals方法。
*/
public class HashSetTest1 {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<>();
Student s1 = new Student("xiaohei", 23);
Student s2 = new Student("xiaohei", 23);
Student s3 = new Student("xiaomei", 22);
hs.add(s1);
hs.add(s2);
hs.add(s3);
for (Student student : hs) {
System.out.println(student);
}
}
}
2 Map集合
2.1 Map集合概述
interface Map<K,V> K:键的类型;V:值的类型
- Interface Map<K,V> K:键的数据类型;V:值的数据类型
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- (键 + 值) 这个整体 我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”。
- 创建Map集合的对象
- 多态的方式
- 具体的实现类HashMap
import java.util.HashMap;
import java.util.Map;
/**
* Map的基本使用
*/
public class MyMap1 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("itheima001", "小智");
map.put("itheima002", "小美");
map.put("itheima003", "大胖");
System.out.println(map);//{itheima003=大胖, itheima001=小智, itheima002=小美}
}
}
2.2 Map集合的基本功能
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
import java.util.HashMap;
import java.util.Map;
/**
* Map的基本方法
*/
public class MyMap2 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("itheima001", "小智");
map.put("itheima002", "小美");
map.put("itheima003", "大胖");
map.put("itheima004", "小黑");
map.put("itheima005", "大师");
// method1(map);
// method2(map);
// method3(map);
// method4(map);
// method5(map);
// method6(map);
// method7(map);
}
private static void method1(Map<String, String> map) {
//V put(K key,V value) 添加元素
//如果要添加的键不存在,那么会把键值对都添加到集合中
//如果要添加的键是存在的,那么会覆盖原先的值,把原先值当做返回值进行返回。
String s = map.put("itheima001", "aaa");
System.out.println(s);//小智
System.out.println(map);//{itheima003=大胖, itheima004=小黑, itheima001=aaa, itheima002=小美, itheima005=大师}
}
private static void method2(Map<String, String> map) {
//V remove(Object key) 根据键删除键值对元素
String s = map.remove("itheima001");
System.out.println(s);//小智
System.out.println(map);//{itheima003=大胖, itheima004=小黑, itheima002=小美, itheima005=大师}
}
private static void method3(Map<String, String> map) {
//void clear() 移除所有的键值对元素
map.clear();
System.out.println(map);//{}
}
private static void method4(Map<String, String> map) {
//boolean containsKey(Object key) 判断集合是否包含指定的键
boolean result1 = map.containsKey("itheima001");
boolean result2 = map.containsKey("itheima006");
System.out.println(result1);//true
System.out.println(result2);//false
}
private static void method5(Map<String, String> map) {
//boolean containsValue(Object value) 判断集合是否包含指定的值
boolean result1 = map.containsValue("aaa");
boolean result2 = map.containsValue("小智");
System.out.println(result1);//false
System.out.println(result2);//true
}
private static void method6(Map<String, String> map) {
//boolean isEmpty() 判断集合是否为空
boolean empty1 = map.isEmpty();
System.out.println(empty1);//false
map.clear();
boolean empty2 = map.isEmpty();
System.out.println(empty2);//true
}
private static void method7(Map<String, String> map) {
//int size() 集合的长度,也就是集合中键值对的个数
int size = map.size();
System.out.println(size);//5
}
}
2.3 Map集合的获取功能
方法名 | 说明 |
---|---|
Set< K > keySet() | 获取所有键的集合 |
V get(Object key) | 根据键获取值 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
K getKey() | 获得键 |
V getValue() | 获得值 |
Collection< V > values() | 获取所有值的集合 |
2.4 Map集合的遍历(方式1)
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map的第一种遍历方式
*/
public class MyMap3 {
public static void main(String[] args) {
//创建集合并添加元素
Map<String, String> map = new HashMap<>();
map.put("1号丈夫", "1号妻子");
map.put("2号丈夫", "2号妻子");
map.put("3号丈夫", "3号妻子");
map.put("4号丈夫", "4号妻子");
map.put("5号丈夫", "5号妻子");
//获取到所有的键
Set<String> keys = map.keySet();
//遍历Set集合得到每一个键
for (String key : keys) {
//通过每一个键key,来获取到对应的值
String value = map.get(key);
System.out.println(key + "---" + value);
}
}
}
2.5 Map集合的遍历(方式2)
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map的第二种遍历方式
*/
public class MyMap4 {
public static void main(String[] args) {
//创建集合并添加元素
Map<String, String> map = new HashMap<>();
map.put("1号丈夫", "1号妻子");
map.put("2号丈夫", "2号妻子");
map.put("3号丈夫", "3号妻子");
map.put("4号丈夫", "4号妻子");
map.put("5号丈夫", "5号妻子");
//首先要获取到所有的键值对对象。
//Set集合中装的是键值对对象(Entry对象)
//而Entry里面装的是键和值
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
//得到每一个键值对对象
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "---" + value);
}
}
}
2.6 Map集合的遍历(方式3)
import java.util.HashMap;
import java.util.Map;
public class MyMap5 {
public static void main(String[] args) {
//创建集合并添加元素
Map<String, String> map = new HashMap<>();
map.put("1号丈夫", "1号妻子");
map.put("2号丈夫", "2号妻子");
map.put("3号丈夫", "3号妻子");
map.put("4号丈夫", "4号妻子");
map.put("5号丈夫", "5号妻子");
map.forEach((String key,String value)->{
System.out.println(key + "---" + value);
});
}
}
3 HashMap集合
3.1 HashMap集合概述和特点
- HashMap是Map里面的一个实现类。
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- HashMap跟HashSet一样底层是哈希表结构的
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法
3.2 案例1 HashMap集合应用案例
需求: 创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。存储三个键值对元素,并遍历
思路:
- 定义学生类
- 创建HashMap集合对象
- 创建学生对象
- 把学生添加到集合
- 遍历集合
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map的练习
*/
public class MyMap6 {
public static void main(String[] args) {
HashMap<Student, String> hm = new HashMap<>();
Student s1 = new Student("xiaohei", 23);
Student s2 = new Student("dapang", 22);
Student s3 = new Student("xiaomei", 22);
hm.put(s1, "江苏");
hm.put(s2, "北京");
hm.put(s3, "天津");
//第一种:先获取到所有的键,再通过每一个键来找对应的值
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("===================================");
//第三种:
hm.forEach(
(Student key, String value) -> {
System.out.println(key + "----" + value);
}
);
}
}
4 TreeMap集合
4.1 TreeMap集合概述和特点
- TreeMap是Map里面的一个实现类。
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- TreeMap跟TreeSet一样底层是红黑树结构的
- 依赖自然排序或者比较器排序,对键进行排序
- 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
4.2 案例2 TreeMap集合应用案例
需求: 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String)。学生属性姓名和年龄,按照年龄进行排序并遍历。
思路:
- 定义学生类
- 创建TreeMap集合对象
- 创建学生对象
- 把学生添加到集合
- 遍历集合
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;
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
/* @Override
public int compareTo(Student o) {
//按照年龄进行降序排序
int result = o.getAge() - this.getAge();
//次要条件,按照姓名排序。
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}*/
}
import java.util.Comparator;
import java.util.TreeMap;
/**
* 需求:创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String)。
* 学生属性姓名和年龄,按照年龄进行排序并遍历。
*/
public class Test1 {
public static void main(String[] args) {
TreeMap<Student, String> tm = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//按照年龄进行升序排序
int result = o1.getAge() - o2.getAge();
//次要条件,按照姓名排序
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
Student s1 = new Student("xiaohei", 23);
Student s2 = new Student("dapang", 22);
Student s3 = new Student("xiaomei", 22);
tm.put(s1, "江苏");
tm.put(s2, "北京");
tm.put(s3, "天津");
tm.forEach(
(Student key, String value) -> {
System.out.println(key + "---" + value);
}
);
}
}
4.3 案例3 TreeMap案例
需求: 请统计字符串“aababcabcdabcde”中每一个字符出现的次数,并按照以下格式输出
- a(5)b(4)c(3)d(2)e(1)
思路:
- Map集合中,键存字符,值存出现的次数
- 遍历字符串,得到每一个字符
- 到集合中看是否包含这个字符
- 如果不包含,表示是第一次出现
- 如果包含,表示不是第一次出现
import java.util.TreeMap;
/**
* 字符串“aababcabcdabcde”
* 请统计字符串中每一个字符出现的次数,并按照以下格式输出
* 输出结果:
* a(5)b(4)c(3)d(2)e(1)
*/
public class Test2 {
public static void main(String[] args) {
String s = "aababcabcdabcde";
TreeMap<Character, Integer> tm = new TreeMap<>();
//遍历字符串,得到每一个字符
for (int i = 0; i < s.length(); i++) {
//c依次表示字符串中的每一个字符
char c = s.charAt(i);
if (!tm.containsKey(c)) {
//表示当前字符是第一次出现。
tm.put(c, 1);
} else {
//存在,表示当前字符已经出现过了
//先获取这个字符已经出现的次数
Integer count = tm.get(c);
//自增,表示这个字符又出现了依次
count++;
//将自增后的结果再次添加到集合中。
tm.put(c, count);
}
}
// a(5)b(4)c(3)d(2)e(1)
//System.out.println(tm);
tm.forEach(
(Character key, Integer value) -> {
System.out.print(key + "(" + value + ")");
}
);
}
}
5 可变参数
- 可变参数:
- 就是形参的个数是可以变化的
- 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
- 方法的参数类型已经确定,个数不确定,我们可以使用可变参数
- 可变参数注意事项
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
- 可变参数定义格式
- 修饰符 返回值类型 方法名(数据类型… 变量名) { }
- 格式:修饰符 返回值类型 方法名(数据类型… 变量名) { }
- 范例:public static int sum(int… a) { }
- 修饰符 返回值类型 方法名(数据类型… 变量名) { }
5.1 案例4 定义一个方法求两个数、三个数的和
/**
* 需求:定义一个方法求两个数的和
* 需求:定义一个方法求三个数的和
*/
public class MyVariableParameter1 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 30;
int sum1 = getSum(a, b);
System.out.println(sum1);
int sum2 = getSum(a, b, c);
System.out.println(sum2);
}
public static int getSum(int a, int b) {
return a + b;
}
public static int getSum(int a, int b, int c) {
return a + b + c;
}
}
5.2 案例5 定义一个方法求N个数的和
/**
* 需求:定义一个方法求N个数的和
*/
public class MyVariableParameter2 {
public static void main(String[] args) {
//在JDK5之前,会把所有的数据都先放到一个数组中
//我们自己定义的方法,形参只要写一个数组就可以了。
int[] arr = {1, 2, 3, 4, 5};
int sum1 = getSum(arr);
System.out.println(sum1);
}
private static int getSum(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum;
}
}
5.3 案例6 可变参数实现-定义一个方法求N个数的和
/**
* 需求:定义一个方法求N个数的和
* 可变参数实现
*/
public class MyVariableParameter3 {
public static void main(String[] args) {
// int[] arr = {1, 2, 3, 4, 5};
// System.out.println(arr);//[I@1b6d3586
int sum1 = getSum(1, 2, 3, 4, 5);
System.out.println(sum1);
}
public static int getSum(int... arr) {
// System.out.println(arr);//[I@4554617c
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum;
}
}
5.4 扩展可变参数使用
/**
* 需求:定义一个方法求N个数的和
* 可变参数实现
*/
public class MyVariableParameter3 {
public static void main(String[] args) {
// int[] arr = {1, 2, 3, 4, 5};
// System.out.println(arr);//[I@1b6d3586
int sum1 = getSum(1, 2, 3, 4, 5);
System.out.println(sum1);
}
public static int getSum(int number, int... arr) {
// System.out.println(arr);//[I@4554617c
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum + number;
}
}
6 创建不可变集合
方法名 | 说明 |
---|---|
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K , V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
- 这个集合不能添加,不能删除,不能修改。
- 但是可以结合集合的带参构造,实现集合的批量添加。
- 在Map接口中,还有一个ofEntries方法可以提高代码的阅读性。
- 首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中。
import java.util.*;
public class MyVariableParameter4 {
public static void main(String[] args) {
//static <E> List<E> of(E…elements) 创建一个具有指定元素的List集合对象
//static <E> Set<E> of(E…elements) 创建一个具有指定元素的Set集合对象
//static <K , V> Map<K,V> of(E…elements) 创建一个具有指定元素的Map集合对象
//method1();
//method2();
//method3();
//method4();
}
private static void method1() {
List<String> list = List.of("a", "b", "c", "d");
System.out.println(list);
//list.add("Q");
//list.remove("a");
//list.set(0,"A");
//System.out.println(list);
// ArrayList<String> list2 = new ArrayList<>();
// list2.add("aaa");
// list2.add("aaa");
// list2.add("aaa");
// list2.add("aaa");
//集合的批量添加。
//首先是通过调用List.of方法来创建一个不可变的集合,of方法的形参就是一个可变参数。
//再创建一个ArrayList集合,并把这个不可变的集合中所有的数据,都添加到ArrayList中。
ArrayList<String> list3 = new ArrayList<>(List.of("a", "b", "c", "d"));
System.out.println(list3);
}
private static void method2() {
//传递的参数当中,不能存在重复的元素。
Set<String> set = Set.of("a", "b", "c", "d", "a");
System.out.println(set);
}
private static void method3() {
Map<String, String> map = Map.of("zhangsan", "江苏", "lisi", "北京", "wangwu", "天津");
System.out.println(map);
}
private static void method4() {
Map<String, String> map = Map.ofEntries(
Map.entry("zhangsan", "江苏"),
Map.entry("lisi", "北京"));
System.out.println(map);
}
}