目录
Set集合
Set系列集合特点:
无序∶添加数据的顺序和获取出的数据顺序不一致;不重复;无索引;
HashSet:无序,不重复,无索引
LinkedHashSet:有序,不重复,无索引
TreeSet:排序,不重复,无索引
HashSet集合的底层原理:
注意:在正式了解HashSet集合的底层原理前,我们需要先搞清楚一个前置知识:哈希值!
哈希值:
就是一个int类型的数值,Java中每个对象都有一个哈希值,可以认为是对象的一个身份证编号。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
对象哈希值的特点:
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
HashSet底层
基于哈希表实现
哈希表是一种增删改查数据,性能都较好的数据结构
JDK8之前HashSet集合的底层原理,基于哈希表:数组+链表
JDK8开始HashSet集合的底层原理,基于哈希表:数组+链表+红黑树
了解一下数据结构(树)
普通二叉树
度:每一个节点的子节点数量
二叉树中,任意节点的度<=2
树高:树的总层数
根节点:最顶层的节点
左子节点
右子节点
左子树
右子树
二叉查找树(二叉排序树)
规则:
小的存左边
大的存右边
一样的不存
二叉查找树存在的问题:
链化
当数据已经是排好序的,导致查询的性能与单链表一样,查询速度变慢!
平衡二叉树(也是二叉查找树)
在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
红黑树
红黑树,就是可以自平衡的二叉查找树
小结:
小结:JDK8开始后,哈希表中引入了红黑树后,进一步提高了操作数据的性能。
深入理解HashSet集合的去重机制
HashSet集合默认不能对内容一样的两个不同对象去重复!
如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复?
/**
* 目标:自定义的类型的对象,比如两个内容一样的学生对象,如果让HashSet集合能够去重复!
*
* 当集合底层结构是哈希表结构,要想让存储的自定义类型对象能够去重,就得对类型进行重写hashCold和equals。
*/
public class SetTest3 {
public static void main(String[] args) {
HashSet<Student> students = new HashSet<>();
students.add(new Student("张三", 18, 180));
students.add(new Student("张三", 18, 180));
System.out.println("students = " + students);
/*
没有重写HashCode和equals方法之前
students = [Student{name='张三', age=18, height=180.0}, Student{name='张三', age=18, height=180.0}]
为什呢
new了两个对象,每个对象的hashCode近似于地址, hash值不同,进一步对数组长度取余运算,得到的位置也不同。两个都会存储下来。
即便是不同的哈希值,取余运算得到的是相同的位置,也会进一步比较equals,此时的equals方法还没有重写,默认比较的是真是的地址是否相同
如果想要达到同类型的不同对象,当属性值都相同是,然后就能够起到去重的效果,就需要重写hashCode和equals方法
*/
}
}
总结:
LinkedHashSet底层原理
依然是基于哈希表(数组,链表,红黑树)实现的
但是,他的每个元素都额外多了一个双链表的机制记录它前后元素的位置。
/**
* @author HuanLe
* @version 1.0
*
* LinkedHashSet的特点:有序,去重,无索引
* 有序:双链表决定
* 去重:哈希表决定
*
* 重点记住特点,用法和Collection一样
*/
public class SetLinkedHashSet {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("AAA");
linkedHashSet.add("AAA");
linkedHashSet.add("BBB");
linkedHashSet.add("CCC");
System.out.println("linkedHashSet = " + linkedHashSet);
}
}
总结
TreeSet
特点:
不重复、无索引、可排序(默认升序排序,按照元素的大小,由小到大排序)
底层是基于红黑树实现的排序。
注意:
对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
对于字符串类型︰默认按照首字符的编号升序排序。
TreeSet自定义排序规则
注意:如果类本身有实现Compaable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。
总结:
注意事项:集合的并发修改异常问题
使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误
集合进阶(二)
Collection类的其他相关知识
前置知识:可变参数
就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;
可变参数的特点和好处
特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
好处:常常用来灵活的接收数据。
可变参数的注意事项:
可变参数在方法内部就是一个数组。
一个形参列表中可变参数只能有一个
可变参数必须放在形参列表的最后面
public class ParamTest {
public static void main(String[] args) {
add();
add(1);
add(1, 2);
//可变参数的底层本质上是数组,可以直接传入数组
add(new int[]{1, 2, 3, 4});
add(1, 2, 3, 4);
}
/*public static void add(int a) {
}
public static void add(int a, int b) {
}*/
public static void add(int... a) {
//使用方式:直接遍历即可,因为本质就是数组
int sum = 0;
for (int e : a) {
sum += e;
}
System.out.println("sum = " + sum);
}
//可变参数在方法中可以有几个? 1个
//因为可变参数必须要在列表中的最后位置
/*public static void add(int a, int... b, String... c) {
}*/
}
Collections类
是一个用来操作集合的工作类
Collections只能支持对list集合进行排序
注意:
本方法可以直接对自定义类型的List集合排序,但自定义类型必须实现了Comparable接口,指定了比较规则才可以
Map集合
认识Map集合
概念
Map集合称为双列集合,格式:{key1=value1 , key2=value2 , key3=value3 ,...,}一次需要存一对数据做为一个元素.
Map集合的每个元素"“key=value"称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做"键值对集合"
Map集合的所有键是不允许重复的,但值可以重复,键和值是——对应的,每一个键只能找到自己对应的值
Map集合在什么业务场景下使用
Map集合体系
Map集合体系的特点
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的
HashMap (由键决定特点):无序、不重复、无索引;(用的最多)
LinkedHashMap(由键决定特点):由键决定的特点︰有序、不重复、无索引。
TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。
总结:
常用方法
为什么要先学习Map的常用方法
Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。
Map常用方法
public class MapTest2API {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
// 1.public V put(K v,V v) 添加元素: 无序,不重复,无索引。
//put拥有添加和修改的能力
//返回值:如果第一次put键值对数据,返回null,如果见是存在的再次put,返回的就是原来键所对应的值
map.put("王宝强", "马蓉");
map.put("宋喆", "马蓉");
map.put("王宝强", "翠花");
System.out.println("map = " + map);
map.put("贾乃亮", "李小璐");
map.put("PGOne", "李小璐");
// 2.public int size():获取集合的大小
System.out.println("map.size() = " + map.size());
// 3、public void clear():清空集合
// map.clear();
// 4.public boolean isEmpty(): 判断集合是否为空,为空返回true ,反之!
System.out.println("map.isEmpty() = " + map.isEmpty());
// 5.public V get(Object key):根据键获取对应值
String value = map.get("贾乃亮");
System.out.println("value = " + value);
// 6. public V remove(Object key):根据键删除整个元素(删除键会返回键的值)
String remove = map.remove("王宝强");
System.out.println("remove = " + remove);
System.out.println("map = " + map);
// 7.public boolean containsKey(Object key): 判断是否包含某个键 ,包含返回true ,反之
boolean containsKey = map.containsKey("奥特曼");
System.out.println("containsKey = " + containsKey);
// 8.public boolean containsValue(Object value): 判断是否包含某个值。
System.out.println("map.containsValue(\"李小璐\") = " + map.containsValue("李小璐"));
// 9.public Set<K> keySet(): 获取Map集合的全部键。
Set<String> keys = map.keySet();
System.out.println("keys = " + keys);
// 10.public Collection<V> values(); 获取Map集合的全部值。
Collection<String> values = map.values();
System.out.println("values = " + values);
// 11.把其他Map集合的数据倒入到自己集合中来。(拓展)
HashMap<String, String> names = new HashMap<>();
names.putAll(map);
System.out.println("names = " + names);
}
}
遍历方式
1.键找值
public static void main(String[] args) {
// 准备一个Map集合。
Map<String, Double> map = new HashMap<>();
map.put("蜘蛛精", 162.5);
map.put("蜘蛛精", 169.8);
map.put("紫霞", 165.8);
map.put("至尊宝", 169.5);
map.put("牛魔王", 183.6);
System.out.println(map);
// 1、获取Map集合的全部键
Set<String> keys = map.keySet();
// 2、遍历全部的键,根据键获取其对应的值
for (String key : keys) {
Double val = map.get(key);
System.out.println("val = " + val);
}
}
2.键值对
public static void main(String[] args) {
Map<String, Double> map = new HashMap<>();
map.put("蜘蛛精", 169.8);
map.put("紫霞", 165.8);
map.put("至尊宝", 169.5);
map.put("牛魔王", 183.6);
System.out.println(map);
// 1、调用Map集合提供entrySet方法,把Map集合转换成键值对类型的Set集合
//调用entrySet
Set<Map.Entry<String, Double>> entrySet = map.entrySet();
//遍历
for (Map.Entry<String, Double> entry : entrySet) {
String key = entry.getKey();
Double value = entry.getValue();
System.out.println(key + ":" + value);
}
}
3.Lambda
/**
* 目标:掌握Map集合的第三种遍历方式:forEach方法遍历【Lambda】。
*/
public class MapTest3 {
public static void main(String[] args) {
Map<String, Double> map = new HashMap<>();
map.put("蜘蛛精", 169.8);
map.put("紫霞", 165.8);
map.put("至尊宝", 169.5);
map.put("牛魔王", 183.6);
//匿名内部类方式
map.forEach(new BiConsumer<String, Double>() {
@Override
public void accept(String s, Double aDouble) {
System.out.println(s + " : " + aDouble);
}
});
//Lambda方式
map.forEach((k, v) -> {
System.out.println(k + " : " + v);
});
}
}