------------javaSE基础加强d1---01-Set集合系列-特点------------------------------------------------------------
小结:
Set集合特点:无序、不重复、无索引;
- HashSet:无序、不重复、无索引;
- LinkedHashSet:有序、不重复、无索引;
- TreeSet:排序、不重复、无索引;
Set要用到的常用方法,基本上就是Collection提供的;自己几乎没有额外新增什么功能。
------------javaSE基础加强d1---02-Set集合系列-底层原理------------------------------------------------------
哈希值:
- 概念:就是int类型的随机值,Java中每个对象都有一个哈希值;
- Java中所有对象都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值;
- public int hashCode( ); 返回对象的哈希码值;
- Java中所有对象都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值;
- 对象哈希值的特点:
- 同一个对象多次调用hashCode( ) 方法返回的哈希值是相同的;
- 不同的对象,它们的哈希值大概率不相等,但也有可能会相等(哈希碰撞)
HashSet集合的底层原理:
- 基于哈希表存储数据的;
- 哈希表:
- JDK8之前,哈希表 = 数组 + 链表;
- JDK8开始,哈希表 = 数组 + 链表 + 红黑树;
- 哈希表是一种增删改查数据,性能都较好的数据结构;
JDK8之前的哈希表:数组 + 链表:
-
首先,在第一次创建HashSet集合的时候,
1.javaSet <String> set = new HashSet<>(); set.add("数据1");会首先创建一个默认长度16的数组,默认加载因子为0.75,数组名为table;
2. 使用元素的哈希值对数组的长度做运算计算出应存入的位置。-
判断当前位置是否为null,如果是null直接存入
-
不是null,有元素,则调用equals方法比较;
-
相等(新老元素相同);不存;
-
不相等,存入数组;
-
JDK8之前新元素存入数组,占老元素位置,老元素挂下边;
-
JDK8开始后,新元素直接挂老元素下面(链表)。
-
-
-
-
扩容机制:当存入的数据超过了 "当前长度" X "加载因子" 默认即超过12:16*0.75那么直接扩容一倍,然后把原先的内存重新根据哈希算法重新分配;
-
JDK8开始,优化一个问题:数据不多但是链表过长:
-
当链表长度超过8;且数组长度>=64时,自动将链表转化成红黑树;
- 红黑树:一个根列表,比如小的往左边存,大的往右边存。
-
-
提高了检索性能。
-
-
红黑树概念:
- 二叉查找树:就是小的往左边放,大的往右边放;
- 问题:当数据已经是排好的,导致查询的性能与单链表一样,查询速度又慢了。
- 平衡二叉树:在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
- 红黑树:就是自平衡的二叉树。比如根节点是黑色,下一个结点就都是红色,红节点下边又都是黑色。
- 每个路径上的黑节点都是一样的多的。
- 增删查改的性能都比较好。
问题:
- 不能重复,没有索引,比较耗内存。
- 每个元素,要存更多的信息,
小结:
- 什么是哈希值?对象的哈希是有什么特点?
- 对象调用Object的hashCode()方法得到的一个随机值;
- HashSet集合的底层原理是什么样的?
- 基于哈希表实现的。
- JDK8之前的,哈希表:底层使用数组+链表组成;
- JDK9开始后,哈希表:底层采用数组+ 链表 + 红黑树组成的。
- 哈希表存储数据的详细流程:
- 创建一个默认长度16。默认加载因子为0.75的数组,数组名为table;
- 根据元素的哈希值跟数组的长度计算出应存入的位置;
- 判断当前位置是否为null;是null直接存;不是null则有元素,调用equals方法比较属性值,一样不存,不一样则存入数组;
- 当数组存满到16*0.75 = 12时,自动扩容,每次扩充成原先的两倍。
------------javaSE基础加强d1---03-Set集合系列-自定义对象去重复------------------------------------------
自定义对象去重复:
- 需求:
- 创建一个存储学生对象的集合,存储多个学生对象,要求:多个学生对象的成员变量值相同时,我们就认为是同一个对象,要求只保留一个。
- 分析:
- 定义学生类,创建HashSet集合对象,创建学生对象
- 把学生添加到集合。
java
//学生类:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
package com.itheima.demo1hashset;
import java.util.Objects;
public class Student {
private String name;
private int age;
private String address;
private String phone;
public Student() {
}
public Student(String name, int age, String address, String phone) {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
}
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 getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public boolean equals(Object o) {
//只要内容一样,就返回true;
//第一步,如果自己和自己比,返回true;//老师的代码第一行是:if (this == o) return true;
//第二步,判断对象是否为空,或者判断对象类型是否一致,不一致就返回false;
if (o == null || getClass() != o.getClass()) return false;
//第三步,将o转换成真正的Student对象,然后进行属性值比较;
Student student = (Student) o;
//第四步,真正的开始比较内容,
return age == student.age && Objects.equals(name, student.name) && Objects.equals(address, student.address) && Objects.equals(phone, student.phone);
}
@Override
public int hashCode() {
//保证了不同学生对象如果内容一样返回的哈希值就是一样的。
return Objects.hash(name, age, address, phone);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}' + "\n";
}
}
//主函数------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
package com.itheima.demo1hashset;
import java.util.HashSet;
import java.util.Set;
public class SetDemo2 {
public static void main(String[] args) {
//目标:掌握HashSet集合去重操作。
Student s1 = new Student("小王", 18, "上海", "123456");
Student s2 = new Student("小王", 18, "上海", "123456");
Student s3 = new Student("张三", 19, "北京", "456789");
Student s4 = new Student("李四", 17, "上海", "789654");
Set<Student> set = new HashSet<>();
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
System.out.println(set);
}
}
小结:
- 如果希望Set集合认为2个内容一样的对象是重复的怎么办?
- 重写对象的hashCode()和equals()方法。
------------javaSE基础加强d1---04-Set集合系列-LinkedHashSet---------------------------------------------
LinkedHashSet:有序、不重复、无索引;
底层原理:
- 依然是基于哈希表(数组、链表、红黑树)实现:
- 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置;

LinkedHashSet集合的底层原理基于LinkedHashMap;
双链表底层源码记录的双地址名字是Entry,也是基于链表的Node实现的。
小结:
- LinkedHashSet集合的特点和原理是怎样的?
- 有序、不重复、无索引;
- 底层基于哈希表,使用双链表记录添加顺序;
------------javaSE基础加强d1---05-Set集合系列-TreeSet---------------------------------------------------------
TreeSet:排序、不重复、无索引;(默认升序,按照元素的大小,从小到大排序)
底层是基于红黑树实现的排序;然后红黑树内部有算法会自动平衡。
注意,排序规则:
- 对于数值类型:Integer,Double,默认按照数值本身的大小进行升序排序;
- 对于字符串类型:默认按照首字母的编号升序排序;
- 对于自定义类型如Student对象,TreeSet默认是无法直接排序的。
- 两种解决方案:
- 方案1:让自定义的类实现Comparable接口,重写里面的compareTo方法来指定比较规则;
- 方案2:通过调用TreeSet集合有参构造器,可以设置Comparator对象(比较器对象,用于指定比较规则)
- public TreeSet( Comparator<? super E> comparator)
- 两种解决方案:
两种方案中,关于返回值的规则:
第一个元素大于第二个元素,返回正整数;二大于一,返回负整数,两个元素相等,返回0,同时集合会认为两个元素相同,只会保留一个元素;
如果类本身实现了Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。
代码演示:
java
//定义教师类:里面实现了Comparable接口;规则是按照年龄排序;------------------------------------------------------------------------------------
package com.itheima.demo1hashset;
import lombok.NoArgsConstructor;
@NoArgsConstructor
//方案1:
public class Teacher implements Comparable<Teacher>{
//public class Teacher{
private String name;
private int age;
private double salary;//薪水
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}' + "\n";
}
//方案1:实现接口,重写方法:
//前置:this是当前对象,o是参数对象;
//如果你认为左边大于右边,返回正整数;右边大于左边,返回负整数;相等返回0;
@Override
public int compareTo(Teacher o) {
// if(this.salary > o.salary) return 1;
// if(this.salary < o.salary) return -1;
// return 0; //薪水相同,返回相等。
return this.getAge() - o.getAge(); //等同于上边三行;不对注意要返回整数。
//注意,返回0的话,TreeSet会认为两个对象是一样的,它就只保留一个。
}
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 double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Teacher(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
// public Teacher() {
// }
}
java
//主函数,写了自定义的Comparator比较器对象,按照薪水排序;------------------------------------------------------------------------------------
package com.itheima.demo1hashset;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo3 {
public static void main(String[] args) {
//目标:搞清楚TreeSet集合对于自定义对象的排序;
//方案2.搞比较器
Set<Teacher> teachers = new TreeSet<>(new Comparator<Teacher>() {
// //会依次触发比较器,存入的对象两两比较,
@Override
public int compare(Teacher o1, Teacher o2) {
// return (o1.getAge() - o2.getAge());
return Double.compare(o1.getSalary(), o2.getSalary());
}
});
// Set<Teacher> teachers = new TreeSet<>();
teachers.add(new Teacher("小王", 18, 5000.9));
teachers.add(new Teacher("老陈", 58, 6000.5));
teachers.add(new Teacher("土豆", 26, 3999.5));
teachers.add(new Teacher("宇智波", 23, 9000));
System.out.println(teachers); //直接bug;因为TreeSet集合默认不能给自定义对象排序,因为它不知道大小规则,两种解决方案。
//1、对象类实现一个Comparable接口,并重写compare方法,按照指定的规则进行排序;
//2、public TreeSet(Comparator c) 集合自带比较器Comparator对象,指定比较规则;
}
}
小结:
- TreeSet集合的特点是怎样的?
- 可排序、不重复、无索引;
- 底层基于红黑树实现排序,增删改查性能较好;
- TreeSet集合对自定义类型的对象排序,有几种方式指定比较规则?
- 2种;
- 类实现Comparable接口,重写比较规则。
- 集合自定义Comparator比较器对象,重写比较规则。
整体集合的"小结":理论
- 如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据 ?
- 用ArrayList集合(有序、可重复、有索引),底层基于数组(常用)
- 希望记住元素的添加顺序,且增删首尾数据 的情况较多?
- LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
- 不在意元素顺序,也没有重复元素需要存储,只希望增删查改都快 呢?
- 用HashSet集合(无序、不重复、无索引),底层基于哈希表实现的(常用)
- 记住元素的添加顺序,没有重复元素需要存储,希望增删改查都快?
- 用LinkedHashSet集合(有序、不重复、无索引),底层基于哈希表和双链表
- 对元素进行排序,没有重复元素需要存储,希望增删查改都快?
- TreeSet集合,基于红黑树实现。
- 真正用的还是那两个,因为ArrayList也有办法让他排序。
------------javaSE基础加强d1---06-Map集合-体系特点-常用方法---------------------------------------------
Map是双列集合,不属于Collection(单列集合)
Map集合:
- Map集合也叫"键值对集合";格式:{key1 = value1,key2 = value2,key13 = value3,...}
- Map集合的所有键是不允许重复的,值可以重复;键和值是一一对应的,每一个键只能找到自己对应的值。
什么业务场景下使用?
- 比如简易版的购物车:
- {商品1 = 2,商品2 = 3,商品3 = 2,商品4 = 3...};
- 需要存储一一对应的数据时,就可以考虑使用Map集合来做。
Map有个根接口:Map<K,V> 键,值;;是一种泛型接口。

HashMap用的相对多一点。
Map集合特点:
- Map集合体系的特点:都是由键决定的,值只是一个附属品,值是不做要求的。
- HashMap特点:无序、不重复、无索引(用的最多)
- LinkedHashMap特点:由键决定的特点:有序、不重复、无索引;
- TreeMap:按照大小默认升序排序、不重复、无索引;
- HashMap特点:无序、不重复、无索引(用的最多)
java
package com.itheima.demo2map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class MapDemo1 {
public static void main(String[] args) {
//目标:认识Map集合的体系特点;
//1、创建Map集合
//HashMap集合特点:无序、不重复、无索引,键值对都可以是null,值不做要求(可以重复);
Map<String,Integer> map = new HashMap<>(); //一行经典代码
map.put("1嫦娥",20);
map.put("2女儿国国王",25);
map.put("3白骨精",18);
map.put("4铁山公主",34);
map.put("5紫霞",19);
map.put("小龙女",26);
map.put("小龙女",18);
map.put(null,null);
System.out.println("map集合:" + map);
//创建LinkedHashMap集合
//LinkedHashMap集合特点:有序、不重复、无索引;
Map<String,Integer> LinkedHashMap = new LinkedHashMap<>();
LinkedHashMap.put("1嫦娥",20);
LinkedHashMap.put("2女儿国国王",25);
LinkedHashMap.put("3白骨精",18);
LinkedHashMap.put("4铁山公主",34);
LinkedHashMap.put("5紫霞",19);
LinkedHashMap.put("小龙女",26);
LinkedHashMap.put("小龙女",18);
LinkedHashMap.put(null,null);
System.out.println("LinkedHashMap集合:" + LinkedHashMap);
//创建TreeMap集合
//TreeMap集合特点:排序、不重复、无索引;不能以null为键;
Map<String,Integer> TreeMap = new TreeMap<>();
TreeMap.put("1嫦娥",20);
TreeMap.put("2女儿国国王",25);
TreeMap.put("3白骨精",18);
TreeMap.put("4铁山公主",34);
TreeMap.put("5紫霞",19);
TreeMap.put("小龙女",26);
TreeMap.put("小龙女",null);
// TreeMap.put(null,null);
System.out.println("TreeMap集合:" + TreeMap);
}
}
小结:
- Map集合是什么?什么时候可以考虑使用Map集合?
- Map集合是键值对集合
- 需要存储一一对应的数据时,就可以考虑使用Map集合来做。
- Map集合的实现类有哪些?各自的特点?
- HashMap:键无序、不重复、无索引,值不做要求;
- LinkedHashMap:键有序、不重复、无索引,值不做要求;
- TreeMap:键排序、不重复、无索引,值不做要求(键不能null)
Map常用方法:
双列集合的祖宗:功能是全部双列集合的都可以继承过来使用:
|--------------------------------------------|-------------------|
| 方法名称 | 说明 |
| public V put (K key,V Value) | 添加元素 |
| public int size() | 获取集合的大小 |
| public void clear() | 清空集合 |
| public boolean isEmpty( ) | 判断集合是否为空,空返回true; |
| public V get (Object key) | 根据键获取对应值 |
| public V remove (Object key) | 根据键删除整个元素 |
| public boolean containsKey(Object key) | 判断是否包含某个键 |
| public boolean containsValue(Object value) | 判断是否包含某个值 |
| public Set<K> keySet() | 获取全部键的集合 |
| public Collection<V> values() | 获取Map集合的全部值 |
功能示范:
java
package com.itheima.demo2map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo2 {
public static void main(String[] args) {
//目标:掌握Map的常用方法。
Map<String,Integer> map = new HashMap<>(); //一行经典代码
map.put("1嫦娥",20);
map.put("2女儿国国王",25);
map.put("3白骨精",18);
map.put("4铁山公主",34);
map.put("5紫霞",19);
map.put("小龙女",26);
map.put("小龙女",18);
map.put(null,null);
System.out.println("map集合:" + map);
//1、根据键取值
System.out.println("1、根据键"小龙女"取值" + map.get("小龙女"));
System.out.println("1、根据键"null"取值" + map.get(null));
//2、判断集合中是否包含某个键
System.out.println("2、判断集合中是否包含某个键"小龙女":" + map.containsKey("小龙女"));
System.out.println("2、判断集合中是否包含某个键"null":" + map.containsKey(null));
//3、判断集合中是否包含某个值
System.out.println("3、判断集合中是否包含某个值"26":" + map.containsValue(26));
System.out.println("3、判断集合中是否包含某个值"null":" + map.containsValue(null));
//4、根据键删除键值对,返回值
System.out.println("4、根据键"小龙女"删除键值对,返回值:" + map.remove("小龙女"));
System.out.println("4、根据键"3白骨精"删除键值对,返回值:" + map.remove("3白骨精"));
System.out.println("输出删除后的集合" + map);
//5、判断集合是否为空
System.out.println("5、判断集合是否为空:" + map.isEmpty());
//6、获取集合的长度
System.out.println("6、获取集合的长度:" + map.size());
//7、获取所有的键放到一个Set集合中。因为Map的键本来就是无序不重复的
System.out.println("7、获取所有的键:" + map.keySet());
// Set<String> keys = map.keySet();
// for (String key : keys) {
// System.out.println("键是:" + key);
// }
//8、获取所有的值放到一个Collection集合中。因为Map的值是无序可能重复的的
System.out.println("8、获取所有的值:" + map.values());
// Collection<Integer> values = map.values();
// for (Integer value : values) {
// System.out.println("值是:" + value);
// }
//清空集合
map.clear();
System.out.println("9清空集合后集合map:" + map);
}
}
------------javaSE基础加强d1---07-Map集合-遍历方式-键值对-键值对---------------------------------------
Map集合的遍历方式:
- 方式1:键找值:
- 先获取Map集合全部的键,再通过遍历键找值:
- 需要用到的方法:
- public Set<K> keySet( ) 获取所有键的集合;
- public V get(Object key) 根据键获取其对应的值;
- 需要用到的方法:
- 先获取Map集合全部的键,再通过遍历键找值:
- 方式2:键值对:
- 把"键值对"看成一个整体进行遍历,先给每个键值对都搞成一个对象,然后用增强for循环遍历出来。
- 需要用到的方法:
- Set<Map.Entry<E,V>> entrySet() 获取所有"键值对"的集合;
- Entry是一个接口,然后给他实现,他会把键值对看成一个整体;
- 增强for直接遍历
- Set<Map.Entry<E,V>> entrySet() 获取所有"键值对"的集合;
- 需要用到的方法:
- 把"键值对"看成一个整体进行遍历,先给每个键值对都搞成一个对象,然后用增强for循环遍历出来。
- 方式3:Lambda表达式(JDK8开始)
- 阿巴
-
需要用到的方法:
- default void forEach(BiConsemer<? super K, ? super V> action) 结合Lambda遍历Map集合;
javamap.forEach(k,v) -> { System.out.pringln(k + "=" + v); });阿巴。
-
- 阿巴
ok现在是代码展示以上三种方式:
java
//先搞一个Map集合做准备。
Map<String,Integer> map = new HashMap<>(); //一行经典代码
map.put("1嫦娥",20);
map.put("2女儿国国王",25);
map.put("3白骨精",18);
map.put("4铁山公主",34);
map.put("5紫霞",19);
map.put(null,null);
-
方式1:键找值:
java//1、遍历方式一:键找值 public static void show1(Map<String,Integer> map){ //1、获取所有的键 Set<String> keys = map.keySet(); //2、根据键获取对应的值 for (String key : keys) { Integer value = map.get(key); System.out.print(key + "=" + value + "\t"); } System.out.println(); } -
方式2:键值对整体遍历:
java//2、遍历方式二:键值对; //把map集合转换成Set集合,里面的元素类型都是键值对类型(Map.Entry<String,Integer>) public static void show2(Map<String,Integer> map){ /** * map = {1嫦娥=20, 2女儿国国王=25, 3白骨精=18, 4铁山公主=34, 5紫霞=19} * ↓ * map.entrySet() * ↓ * Set<Map.Entry<String, Integer>> entries = [(嫦娥=20), (2女儿国国王=25), (3白骨精=18), (4铁山公主=34), (5紫霞=19)] */ Set<Map.Entry<String, Integer>> entries = map.entrySet(); //现在集合中元素类型是Map.Entry<String, Integer>,一个键值对就是一个对象,那么就可以用增强for循环遍历 for (Map.Entry<String, Integer> entry : entries) { System.out.print(entry.getKey() + "=" + entry.getValue() + "\t"); } System.out.println(); } -
方式三:Lambda表达式遍历
java//3、Lambda表达式遍历; public static void show3(Map<String,Integer> map){ // map.forEach(new BiConsumer<String, Integer>() { // /** // * 以下是forEach方法中的代码 // * default void forEach(BiConsumer<? super K, ? super V> action) { // * Objects.requireNonNull(action); 首先进行判断这个键值对整体是不是空,不是空就搞成一个对象action // * for (Map.Entry<K, V> entry : entrySet()) { 既然是对象,那就可以用增强for循环遍历 // * K k; 赋值 // * V v; 赋值 // * try { // * k = entry.getKey(); 给k赋值键 // * v = entry.getValue(); 给v赋值值 // * } catch (IllegalStateException ise) { 异常则执行 // * // this usually means the entry is no longer in the map. // * throw new ConcurrentModificationException(ise); // * } // * action.accept(k, v); 把键和值传给accept方法,执行(即自己重写的accept方法) // * } // * } // */ // @Override // public void accept(String key, Integer value) { // System.out.print(key + "=" + value + "\t"); // } // }); //Lambda表达式简化: // map.forEach((key,value)->{ // System.out.print(key + "=" + value + "\t"); // }); //方法引用简化: map.forEach((k,v) -> System.out.print(k + "=" + v + "\t")); }
------------javaSE基础加强d1---08-Map集合-遍历方式三- Lambda------------------------------------------
//一起写到上边07里了。
------------javaSE基础加强d1---09-Map集合-综合案例------------------------------------------------------------
统计投票信息:需求:
- 需求:
- 某个班级80名学生,现在需要组织秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人最多。
- 分析:
- 将80个学生选择的数据拿到程序中去[A,B,A,C,D,C,A......]
- 准备一个Map集合用于存储统计的结果Map<String , Integer>,键是经典,值代表投票数量;
- 遍历80个学生选择的景点,每遍历一个景点,看Map集合中是否存在该景点,不存在存入"景点=1",存在则其对应的值+1;
java
package com.itheima.demo2map;
import java.util.*;
public class MapTese6 {
public static void main(String[] args) {
//目标:完成Map集合相关的案例:投票统计程序
calc();
}
private static void calc() {
//1、把80个学生选择的景点数据拿到程序中来,才可以统计;
List< String> locations = new ArrayList<>();
String[] names = {"玉龙雪山","少林寺","天安门","泰山"};
Random r = new Random();
for (int i = 0; i < 5000; i++) {
int index = r.nextInt(names.length);
switch ( index){
case 0:
locations.add("玉龙雪山");
break;
case 1:
locations.add("少林寺");
break;
case 2:
locations.add("天安门");
break;
case 3:
locations.add("泰山");
break;
default:
break;
}
}
//2、统计每个景点被选择的次数:
Map<String,Integer> map = new HashMap<>();
for(String location : locations){ //遍历集合中的数据
// if(map.containsKey(location)){ //判断集合中是否包含这个景点键
// Integer count = map.get(location); //获取这个景点对应的值
// map.put(location,count+1); //重新存,并且把值+1
// }else{
// map.put(location,1); //如果不存在,说明之前没有统计过,就添加,值为1
// }
//利用三元运算符简化:
map.put(location,map.containsKey(location) ? map.get(location)+1 : 1);
}
System.out.println(map);
}
}
------------javaSE基础加强d1---10-Map集合-实现类---------------------------------------------------------------
三种Map集合:
- HashMap:无序、不重复、无索引(用的最多);
- 实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
- LinkedHashMap:有序、不重复、无索引;
- 实际上:之前学的LinkedHashSet集合的底层原理就是LinkedHashMap;
- TreeMap:默认升序、不重复、无索引;
- 原理:TreeMap和TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序;
- 底层依然是创建一个TreeMap对象。只不过只要键不要值。
- 记得给比较规则或比较器
- 原理:TreeMap和TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序;
java
package com.itheima.demo2map;
import com.itheima.demo1hashset.Teacher;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class MapDemo7 {
public static void main(String[] args) {
//目标:TreeMap集合(原理和用法与TreeSet集合一样)
Map<Teacher,String> map = new TreeMap<>((o1, o2) -> Double.compare(o2.getSalary(), o1.getSalary()));
map.put(new Teacher("小龙女",18,5000),"小龙女"); //每个老师对象是一个键,后边的字符串是值。 map.put("1嫦娥",20);
map.put(new Teacher("小王", 19, 5005.9),"小王");
map.put(new Teacher("老陈", 58, 6000.5),"老陈");
map.put(new Teacher("土豆", 26, 3999.5),"土豆");
map.put(new Teacher("宇智波", 23, 9000),"宇智波");
//因为在teacher类中已经规定了升序规则,所以这里不需要指定比较器
System.out.println(map);
//因为在老师类中重写了toString方法,所以这里打印出来的是老师对象键之后秒跟回车。
}
}
------------javaSE基础加强d1---11-Stream流-体验-获取Stream流---------------------------------------------
Stream流:
- 从JDK8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据。
- 优势:
- Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好。
- 使用步骤:
- 1、准备数据,获取这个集合或者数组的stream流(好像一个流水线);
- 可以调用流水线的各种方法,对数据进行处理、计算。(过滤、排序、去重...)
- 获取处理的结果,遍历、统计、收集到一个新的集合中返回。
体验Stream流:
- 需求:
-
代码:
-
把集合中所有以"张"开头,且是三个字的元素存储到一个集合。
javapackage com.itheima.demo3stream; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class StreamDemo1 { public static void main(String[] args) { //目标:认识Stream流,掌握其基本使用步骤,体会它的优势和特点: List<String> list = new ArrayList<>(); //张无忌、周芷若、赵敏、张强、张三丰、张翠山 list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰"); list.add("张翠山"); System.out.println("list的集合" + list); //1、传统方案:找出姓张的人,名字为三个字,有的话就存到一个新的集合中。 List<String> list2 = new ArrayList<>(); for(String name: list){ if(name.startsWith("张") && name.length()==3){ list2.add(name); } } System.out.println("list2的集合" + list2); //2、使用Stream流 List<String> list3 =list.stream() //list.stream()第一步,用集合或数组创建一个Stream流(就好像把这里面的数据放到一个传送带上),然后调用传送带的方式筛选数据。 .filter(s -> s.startsWith("张")) //每个数据都是这个s, .filter(s -> s.length()==3). collect(Collectors.toList()); //添加到新的集合中 System.out.println("list3的集合" + list3); } }
-
小结:
- Stream流是什么?有什么用?结合了什么技术?
- 简化集合、数组操作的API。结合Lambda表达式;
- 说说Stream流处理数据的步骤是什么?
- 先得到集合或数组的Stream流;
- 调用Stream流的方法对数据进行处理;
- 获取处理的结果。
获取Stream流
public interface Stream<T>...{}
-
获取集合的Stream流:
1.|------------------------------|-----------------|
| Collection提供的如下方法 | 说明 |
| default Stream<E> Stream() | 获取当前集合对象的Stream | -
获取数组的Stream流:
1.|------------------------------------------------------|------------------|
| Arrays类提供的方法 | 说明 |
| public static <T> Stream<T> stream(T[ ] array) | 获取当前数组的Stream流 |
| Stream类提供的方法 | 说明 |
| public static<T> Stream of (T ... values) | 获取当前接收数据的Stream流 |其中 (T ... values)指可变内容
java
package com.itheima.demo3stream;
import java.util.*;
import java.util.stream.Stream;
public class StreamDemo2 {
public static void main(String[] args) {
//目标:获取Stream流的方式:
//1、获取集合的Stream流,调用集合提供的stream()方法
List<String> list = new ArrayList<>();
Stream< String> s1 = list.stream();
Map<String,Integer> map = new HashMap<>();
// map.stream; //这肯定报错呀,因为stream流是Collection接口的子接口,所以不能用map.stream()直接调。
//那怎么获取呢?先拿一个键流:
map.keySet().stream(); //拿到键集合,再拿键集合的流
Stream< String> s2 = map.keySet().stream(); //拿到一个键集合嘛。
Stream< Integer> s3 = map.values().stream(); //拿到一个值集合
//拿键值对流?
//把键值对集合转换成Set集合,里面的元素类型是键值对类型(Map.Entry<String,Integer>)
Stream< Map.Entry<String,Integer>> s4 = map.entrySet().stream();
//2、获取数组的Stream流,调用Arrays类中的静态方法stream()
String[] arr = {"1嫦娥", "2女儿国国王", "3白骨精", "4铁山公主", "5紫霞"};
Stream< String> s5 = Arrays.stream(arr);
System.out.println("s5.count():" + s5.count()); //拿到5个数据吧
Stream< String> s6 = Stream.of(arr); //里面很灵活,可以送数组,可以给多个参数。
Stream< String> s7 = Stream.of("1嫦娥", "2女儿国国王", "3白骨精", "4铁山公主", "5紫霞");
Stream< String> s8 = Stream.of(); //不给都行。
}
}
------------javaSE基础加强d1---12-Stream流-常用中间方法---------------------------------------------------
stream流的中间方法:
中间方法指的是:调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。
|-----------------------------------------------------------------|------------------|
| Stream提供的常用中间方法 | 说明 |
| Stream<T> filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 |
| Stream<T> sorted( ) | 对元素进行升序排序 |
| Stream<T> sorted( Comparator <? super T> comparator) | 按照指定规则排序 |
| Stream<T> limit(long maxSize) | 获取前几个元素 |
| Stream<T> skip(long n) | 跳过前几个元素 |
| Stream<T> distinct( ) | 去除流中重复的元素。 |
| <R> Stream<R> map(Function<? super T,? extends R> mapper) | 对元素进行加工,并返回对应的新流 |
| static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
java
package com.itheima.demo3stream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamDemo3 {
public static void main(String[] args) {
//目标:掌握Stream提供的常用方法,对流上的数据进行处理(返回新流,支持链式编程)
List<String> list = new ArrayList<>();
//张无忌、周芷若、赵敏、张强、张三丰、张翠山
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张翠山");
System.out.println("list的集合" + list);
//提取到Stream流中。
//1、过滤方法:
System.out.print("1、过滤方法:\n ");
list.stream().filter(s -> s.startsWith("张")&&s.length()==3).forEach(s -> System.out.print(s + " "));
//forEach方法:是一个遍历方法,不是一个中间方法,是一个遍历方法,遍历完就不会返回流了。会直接结束。
//2、排序方法:
List<Double> list2 = new ArrayList<>();
list2.add(99.9);
list2.add(9.9);
list2.add(96.9);
list2.add(100.9);
System.out.println("list2:" + list2);
System.out.print("2、排序方法:\n ");
list2.stream().sorted().forEach(s -> System.out.print(s + " "));
System.out.println();
//3、现在用自定义规则搞一个降序:
System.out.print("3、现在用自定义规则搞一个降序:\n ");
list2.stream().sorted((s1,s2) -> Double.compare(s2,s1)).forEach(s -> System.out.print(s + " "));
//这里用到了一个double两个数比较大小的方法:Double.compare(double a,double b)。
System.out.println();
//4、获取前几个元素:
System.out.print("4、获取前几个元素:\n ");
list.stream().limit(3).forEach(s -> System.out.print(s + " "));
System.out.println();
//5、跳过几个元素:
System.out.print("5、跳过几个元素:\n ");
list.stream().skip(3).forEach(s -> System.out.print(s + " "));
System.out.println();
//6、去重:
System.out.print("6、去重:\n ");
list.stream().distinct().forEach(s -> System.out.print(s + " "));
System.out.println();
//7、映射/加工方法:把原来流上的数据拿出来变成新数据再放到流上:
System.out.print("7、映射/加工方法:\n ");
list2.stream().map(s -> s+10).forEach(s -> System.out.print(s + " "));
System.out.println();
//8、合并流://注意,这里一次只能合并两个流。
System.out.print("8、合并流:\n ");
Stream<String> s1 =list.stream();
Stream<Double> s2 =list2.stream();
Stream<Object> s3 = Stream.concat(s1,s2); //两个流的类型不同,所以要用Object类型;如果两个流的类型一样,就直接写类型就可以。
s3.forEach(s -> System.out.print(s + " "));
}
}
------------javaSE基础加强d1---13-Stream流-常用终结方法---------------------------------------------------
- 终结方法:调用完成后,不会再返回Stream了,没法继续使用流了。
|-------------------------------------------------------|---------------|
| Stream提供的常用终结方法 | 说明 |
| void forEach(Consumer action) | 对此流运算后的元素执行遍历 |
| long count( ) | 统计此流运算后的元素个数 |
| Opional<T> max(Comparator<? super T> comparator) | 获取此流运算后的最大值元素 |
| Optional<T> min(Comparatir<? super T> comparator) | 获取此流运算后的最小值元素 |
注意:这里取的最大值和最小值不是直接返回的,是给一个Optional对象,这个对象可以装null返回的。
3. 收集方法:因为开发中传数据不会传一个Stream流,一般都是传数组集合什么的。
-
收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。
-
Stream流:方便操作集合/数组的手段;集合/数组:才是开发中的目的。
|--------------------------------|----------------------|
| Stream提供的常用终结方法 | 说明 |
| R collect(Collector collector) | 把流处理后的结果收集到一个指定的集合中去 |
| Object [ ] toArray( ) | 把流处理后的结果收集到一个数组中去。 |
1
7.
|-------------------------------------------------------------------------|----------------|
| Collectors 工具类提供了具体的收集方式 | 说明 |
| public static <T> Collector toList( ) | 把元素收集到List集合中去 |
| public static <T> Collector toSet( ) | 把元素收集到Set集合中 |
| public static Collector toMap(Function keyMapper, Function valueMapper) | 把元素收集到Map集合中 |
PS:Stream流只能收集一次;收集完成之后就空了。
假如说你既要收集到Arrays集合里,又要收集到Set集合里;那建议先收集到Arrays集合,然后再赋值给Set集合一份。
收集到Map集合会报错,因为系统不知道你要收集什么信息到Map集合中去,即什么信息存为键什么信息存为值?
java
//ok现在把stream流搞到集合或者数组中去;
List<String> array1 = new ArrayList<>();
//张无忌、周芷若、赵敏、张强、张三丰、张翠山
array1.add("张无忌");
array1.add("周芷若");
array1.add("赵敏");
array1.add("赵敏");
array1.add("张强");
array1.add("张三丰");
array1.add("张翠山");
//1、用Stream提供的方法搞成集合:
//搞一个Arrays集合接。
System.out.print("1、用Stream提供的方法搞成Arrays集合:\n ");
List<String> array2 = array1.stream().map(s -> s + "开飞机").collect(Collectors.toList());
System.out.println(array2);
//Stream只能收集一次。
//搞一个Set集合接:
System.out.print("1、用Stream提供的方法搞成Arrays集合:\n ");
Set<String> array3 = array1.stream().map(s -> s + "骑ct").collect(Collectors.toSet());
System.out.println(array3);
//收集到数组里:
System.out.print("1、用Stream提供的方法搞成Arrays集合:\n ");
Object[] array4 = array1.stream().map(s -> s + "踢ct").toArray();
System.out.println(Arrays.toString(array4));
//收集到Map集合中:
Stream<Teacher> listn = list.stream(); //不要排序,本来就是乱序的:.sorted((o1, o2) -> Double.compare(o2.getSalary(),o1.getSalary()))
// Map<String,Double> map = listn.collect(Collectors.toMap(Teacher::getName,Teacher::getSalary));
// System.out.println("1、用Stream提供的方法搞成Map集合:\n " + map);
//Map需要键和值;;
//上边代码是简写的,本来应该:
// Map<String,Double> map2 = list.stream().collect(Collectors.toMap(new Function<Teacher,String>() {
// @Override
// public String apply(Teacher teacher) {
// return teacher.getName();
// }
// }, new Function<Teacher,Double>() {
// @Override
// public Double apply(Teacher teacher) {
// return teacher.getSalary();
// }
// }));
// System.out.println("1、用Stream提供的方法搞成Map集合:\n " + map2);
//简化一手:
Map<String,Double> map3 = list.stream().collect(Collectors.toMap(t ->t.getName(),t->t.getSalary()));
System.out.println("1、用Stream提供的方法搞成Map集合:\n " + map3);
System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------");
------------javaSE基础加强d1---14-综合实战-前置知识-可变参数-Collections------------------------------
- 前置知识:
- 可变参数:一种特殊的参数,定义在方法、构造器的形参列表里,
- 格式是:数据类型...参数名称;
- 特点:可以不传参数给它,可以传一个或者同时传多个数据给他;也可以传一个数组给它。
- 好处:常常用来灵活的接收数据。
- 注意事项:
- 可变参数在方法内部就是一个数组;
- 一个形参列表中可变参数只能有一个;
- 可变参数必须放在形参列表的最后边。
- 可变参数:一种特殊的参数,定义在方法、构造器的形参列表里,
工具类:Collections
-
用来操作集合的工具类:
-
提供的静态方法:
|---------------------------------------------------------------------------|-------------------------------|
| 方法名称 | 说明 |
| public static <T> boolean addAll(Collection<? super T>c,T...elemnts) | 给集合批量添加元素 |
| public static void shuffle(List<?> list) | 打乱List集合中的元素顺序 |
| public static <T> void sort(List<T> list) | 对List集合中的元素进行升序排序 |
| public static<T> void sort (ListM<T> list,Collparator<? super T> c) | 对List集合中的元素,按照比较器对象指定的规则进行排序。 |public static <T> void sort(List<T> list)可以直接对自定义类型的List集合排序,但自定义类型必须实现了Collparable接口,指定了比较规则才可以。
------------javaSE基础加强d1---15-综合实战-做牌-对牌排序---------------------------------------------------
- 需求:开发一个斗地主游戏:
- 分析:
- 总共54张牌;
- 点数3~2
- 四种花色
- 大小王;
- 斗地主:发51张牌,留下三张底牌;
- 分析实现:
- 在启动游戏房间的时候,提前准备好54张牌;
- 接着,需要完成洗牌、发牌、对牌顺序、看牌。
代码:
-
首先是 牌类,每一个牌都是一个对象;
-
然后注意,定义了一个整数型num,这个来区分牌的大小,方便排序。
-
其次,重写了toString方法,打印的时候只打花色的点数,不打num
java//首先是定义Card牌类;每个牌都是一个对象。 package com.itheima.demo4test; public class Card { private String size; private String color; private int num; @Override public String toString() { return color + size; } public Card() { } public Card(String color, String size, int num) { this.size = size; this.color = color; this.num = num; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } }牛
-
-
然后是房间类Room:
-
流程都在这里,以及写的一些方法啥的
-
这里是简化了代码,比如直接定义了三个对象来假装打牌,令狐冲、令狐白、令狐黄......
javapackage com.itheima.demo4test; import java.util.*; import java.util.stream.Collectors; public class Room { private List<Card> allCards = new ArrayList<>(); { //准备四种花色 String[] colors = {"♥","♠","♣","♦"}; //准备13张牌 String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"}; int num = 0; for (String number : numbers) { num++; for (String color : colors) { allCards.add(new Card(color,number, num)); } } // allCards.add(new Card("小","王!",14)); // allCards.add(new Card("大","王!",15)); //减少一行............ Collections.addAll(allCards,new Card("小","王!",14),new Card("大","王!",15)); System.out.println(allCards); } //1、启动房间的时候,准备好54张牌。 public void start() { //2、初始化54张牌; //3、洗牌 Collections.shuffle(allCards); System.out.println(allCards); //4、发牌 定义三个玩家:令狐冲、令狐白、令狐黄 Map<String,List<Card>> players = new HashMap<>(); List<Card> lhc = new ArrayList<>(); players.put("令狐冲",lhc); List<Card> lhb = new ArrayList<>(); players.put("令狐白",lhb); List<Card> lhh = new ArrayList<>(); players.put("令狐黄",lhh); //发牌只发51张哦 for (int i = 0; i < allCards.size() - 3; i++) { Card card = allCards.get(i); switch (i % 3) { case 0: lhc.add(card); break; case 1: lhb.add(card); break; case 2: lhh.add(card); break; } } //还有三张底牌。 List<Card> lastCards = allCards.subList(allCards.size() - 3,allCards.size()); System.out.println(lastCards); //谁抢到地主之后给: lhh.addAll(lastCards); //5、对牌 sortCards(lhc); sortCards(lhb); sortCards(lhh); // System.out.println("12313213:::" + lhc); //面向对象设计:给牌类搞一个大小; //6、看牌 for (Map.Entry<String, List<Card>> entry : players.entrySet()){ //获取玩家的名字: String name = entry.getKey(); //获取玩家的牌: List<Card> cards = entry.getValue(); //遍历玩家的牌: System.out.println(name + "的牌是:" + cards); } } //牌排序 private void sortCards(List<Card> cards){ Collections.sort(cards, new Comparator<Card>() { @Override public int compare(Card o1, Card o2) { //先比较牌大小 return o1.getNum() - o2.getNum(); } }); } }过程都写在初始化里了。
-
-
主函数:
javapackage com.itheima.demo4test; public class Geam { public static void main(String[] args) { //开发一个斗地主游戏; //1、每个牌都是一个对象。 //2、游戏房间也是一个对象:定义房间类。 Room r = new Room(); r.start(); } }