文章目录
Map接口
/*
Map接口中常用方法:
1.Map和Collection没有继承关系
2.Map集合以key和value的方式去存储数据:键值对
key和value都是引用数据类型
key和value都是存储对象的内存地址
key起到主导的地位,value是key的一个附属品
3.Map接口中常用方法:
V put(K key,V value);向Map集合中添加键值对
V get(Object key);通过Key获取某个value
void clear();清空Map集合
boolean containsKey(Object key);判断Map中是否包含某个key
boolean containsValue(Object value);判断Map中是否包含某个Value
boolean isEmpty();判断Map集合中的元素个数时候为0
Set<K> KeySet();获取Map集合所有的key(所有的键是一个set集合)
V remove(Object key);通过key删除键值对
int size();获取键值对的个数
Collection<V> values();获取Map集合中所有的value,返回一个Collection
Ser<Map.Entry<K,V>> entrySet();将Map集合转换成Set集合
假设现在有这样一个集合:
map1集合:
key value
---------------------
1 zhangsan
2 lisi
3 wangwu
4 zhaoliu
Set set =map1.entrySet();
set集合对象:
1=zhangsan 【注意:Map集合通过entrySet()方法转换成的这个set集合,set集合中的元素类型是Map.Entry<k,v>】
2=lisi 【Map.Entry和String一样,都是一个类型的名字,只不过:Map,Entry是静态内部类,是Map中的静态内部类】
3=wangwu
4=zhaoliu
*/
public class MapTest01 {
public static void main(String[] args) {
//创建一个Map集合对象
Map<Integer,String> map =new HashMap<Integer,String>();
//向Map集合中添加一个键值对
map.put(1,"张三"); //1在这里进行了自动装箱
map.put(2,"lisi");
map.put(3,"wangwu ");
map.put(4,"zhaoliu");
//通过key获取value
String str=map.get(2);
System.out.println(str);
//获取键值对的数量
System.out.println("键值对的数量"+map.size());
//通过key删除key-value
map.remove(2);
System.out.println("键值对的数量"+map.size());
//判断是否包含某个key
//Contains方法底层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法。
System.out.println(map.containsKey(3));
//判断是否包含某个value
System.out.println(map.containsValue("张三"));
//获取所有的value
Collection<String> values =map.values();
for (String value : values) {
System.out.println(value);
}
}
}
遍历的两种方法
/*
Map集合的遍历
*/
public class MapTest02 {
public static void main(String[] args) {
//第一种方式;获取所有的key,通过遍历key,来遍历value
Map<Integer,String> map =new HashMap<>();
map.put(1,"张三");
map.put(2,"lisi");
map.put(3,"wangwu ");
map.put(4,"zhaoliu");
//遍历Map集合
Set<Integer> set = map.keySet();
//遍历key,通过key获取value
//迭代器可以
Iterator<Integer> it =set.iterator();
while(it.hasNext()){
//取出一个key
Integer key = it.next();
//通过key获取value
String value=map.get(key);
System.out.println(key+ "=" + value);
//第二种方式: Set<Map.Entry<K,V>> entrySet();将Map集合转换成Set集合
//以上这个方法是直接把Map集合转换成Set集合
//Ser集合中元素的类型是:Map.Entry
Set<Map.Entry<Integer,String>> set1= map.entrySet();
//遍历Set集合,每一次取一个Node
//迭代器
Iterator<Map.Entry<Integer,String>> it2=set1.iterator();
while(it2.hasNext()){
Map.Entry<Integer,String> node=it2.next();
Integer key2 =node.getKey();
String value2=node.getValue();
System.out.println(key2+"="+value);
}
//foreach
//这种效率比较高,因为获取key和value都是直接从node对象中获取的属性值
//这种方法比较适合大数据量
for(Map.Entry<Integer,String> node :set1){
System.out.println(node.getKey()+"="+node.getValue());
}
}
}
}
HashMap集合
在JDk8之后,如果哈希表单向链表中元素超过八个, 单向链表这种数据结构会标称红黑树数据结构,当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表
这种方式也是为了提高检索效率,二叉树检索会再次缩小扫描范围
初始化容量是16,默认加载因子是0.75
知识点:
/*
HashMap集合:
1.HashMap集合的底层是:哈希表/散列表的数据结构
2.哈希表是怎样一个数据结构呢?
哈希表是一个数组和链表的结合体
哈希表将数组和单向链表两种数据结构混合在一起,充分发挥他们各自的优点
3.HashMap集合底层的源代码:
public class HashMap{
//HashMap底层实际上就是一个数组(一维数组)
Node<k,v>[] table;
//静态的内部类HashMap.Node
static class Node<K,V>{
final int hash;//哈希值,(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数、算法,可以转换储存成数组的下标)
final K key;//存储到Map集合中的key
V value;//存储到Map集合中的value
Node<K,V> next ;//下一个节点的内存地址
}
}
哈希表:一维数组,这个数组中每一个元素是一个单项链表(数组和链表的结合体)
4.最主要掌握的是:
map.put(k,v);
v=map.get(k);
以上这两个方法的实现原理,是必须要掌握的
5.HashMap集合的key部分特点:
无序,不可重复
为什么无序? 因为不一定挂到了哪个单向链表上
不可重复是怎样保证的? equals方法来保证HashMap集合的key不可重复
如果key重复了,value会覆盖。
放在HashMap集合key部分的元素其实就是放到HashMap集合中了
所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法
6.哈希表HashMap使用不当时无法发挥全部性能!
假设将所有的HashCode()方法返回值固定为某个值,那么会导致哈希表变成了单向链表。
这种情况我们称为散列分布不均匀。
什么是散列分布均匀?
假设有100个元素,10个单项链表,那么每个单项链表上有10个节点,这是最好的,
是散列分布均匀的
假设将所有的hashcode()方法返回值都设定为不一样的值,有什么问题?
这样的话会变成了一维数组
散列分布均匀要求你重写那hashCode()方法时有一定的技巧。
7.重点:放在HashMap集合key部分的元素,以及放在hashSet集合中的元素,需要同时从写hashCode和equals方法。
8.HashMap集合的默认初始化容量是16,默认加载因子是0.75,意为当HashMap集合底层数组的容量到达75%的时候,数组开始扩容
重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到三裂君越,为了提高HashMap集合的存取效率,所必需的。
*/
HashCode的数据结构
重写HashCode方法
-
如果一个类的equals方法重写了,那么hashcode()方法必须重写。并且equals方法返回的是true,hashcode()方法返回的值必须一样。equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的,所以hashCode()方法的返回值也应该相同。
-
HashCode()方法和equals()方法不需要研究了,直接使用IDEA工具生成,但是这两个方法都需要同时生成
-
结论:
放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法
-
对于哈希表数据结构来说:
如果o1和o2的hash值相同,一定是放到同一个单向链表上
当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后的转换的数组下标可能相同,此时会发生“哈希碰撞”。
-
扩容之后是原容量的二倍
HashMap和HashTable的区别
-
HashTable的Key值和value都是不能为null的
hashMap的key值和value都是以可以为null的 -
HashTable方法中都带有synchronized:线程安全的
线程安全有其他的方案,这个方案的效率较低,使用较少了 -
HashMap和HashTable底层都是hash表数据结构
HashTable的初始化容量是11,默认加载因子是0.75
HashTable下的Properties类
/*
目前只需要掌握Properties属性类的相关方法即可
Properties是一个Map集合,继承HashTable,Properties的key和value都是String类型的
Properties被称为属性类对象
Properties是线程安全的
*/
public class PropertiesTest01 {
public static void main(String[] args) {
//创建一个Properties对象
Properties pro =new Properties();
//需要掌握Properties的两个方法,一个存一个取出
pro.setProperty("Username","hahaha");
pro.setProperty("Password","1234565");
//通过key来获取value
String Username= pro.getProperty("Username");
String PassWord =pro.getProperty("Password");
System.out.println(Username); //hahaha
System.out.println(PassWord);//1234565
}
}
TreeSet集合
/*
1.TreeSet集合的底层实际上是一个TreeMap
2.TreeSet集合底层是一个二叉树
3.放到TreeSet集合中的元素等同于放到TreeMap集合Key部分
4.TreeSet集合中的元素:无需不可重复,但是可以按照元素的大小顺序自动排序,
称为:可排序集合
对于自定义类,TreeSet可以排序吗?
无法排序,因为没有指定自定义类的排序比较规则。无法比较谁大谁小
需要实现java.lang.Compareable接口
*/
public class TreeSetTest01 {
public static void main(String[] args) {
TreeSet<String> ts=new TreeSet<>();
//添加元素
ts.add("zhangsan ");
ts.add("lisi");
ts.add("wangwu");
//按照字典顺序自动排序,升序!
for (String t : ts) {
System.out.println(t);
}
}
}
-
对于自定义类,TreeSet可以排序吗?
无法排序,因为没有指定自定义类的排序比较规则。无法比较谁大谁小需要实现java.lang.Compareable接口
自定义类的比较规则
public class TreeSetTest02 {
public static void main(String[] args) {
Person p1 =new Person(30);
Person p2 =new Person(40);
Person p3 =new Person(35);
TreeSet ts =new TreeSet();
ts.add(p1);
ts.add(p2);
ts.add(p3);
for (Object t : ts) {
System.out.println(t);
}
}
}
//放在TreeSet集合中的元素需要实现java.lang.Comparable接口
//并且需要实现compareTo方法。equals可以不写
class Person implements Comparable<Person>{
int age;
public Person(int age){
this.age=age;
}
//需要再这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较
//k.compareTo(t.key)
//拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 或者0
//比较规则最终还是由程序员来定
@Override
public int compareTo(Person p) { //p1.compareTo(p2)
//this是p1
//p是p2
//p1和p2比较的时候,就是this和c比较
return this.age-p.age;
}
public String toString(){
return "Person[age="+age+"]";
}
}
自平衡二叉树数据结构
-
子彭亨二叉树,遵循左小右大的原则存放
-
遍历二叉树的时候有三种方式:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
注意:前中后说的是根的位置
-
TreeSet集合/TreeMap集合采用的是:中序遍历方式
Iterator迭代器采用的是中序遍历方式
-
100 200 60 50 80 12 0140 130 135 180 666…
TreeSet排序方式2:比较器
public class TreeSetTest03 {
public static void main(String[] args) {
//创建treeSet集合的时候,需要使用比较器
// TreeSet<Wugui> wuGuis = new TreeSet<>(); 这样不行,没有通过构造方法传递一个比较器进去
//给构造方法传递一个比较器
TreeSet<Wugui> wuGuis = new TreeSet<>(new WuguiComparator());
//同样也可以使用匿名内部类的方式
TreeSet<Wugui> wuGuis2 = new TreeSet<>(new Comparator<Wugui>() {
@Override
public int compare(Wugui o1, Wugui o2) {
return o1.age-o2.age;
}
});
}
}
class Wugui{
int age;
public Wugui(int age){
this.age =age;
}
@Override
public String toString(){
return "小乌龟{" +
"age=" + age +
'}';
}
}
//再这里单独编写一个比较器
//比较器实现java.util.Comparator接口。
class WuguiComparator implements Comparator<Wugui>{
@Override
public int compare(Wugui o1, Wugui o2) {
//提供比较规则
//按照年龄排序
return o1.age-o2.age;
}
}
比较器和实现接口的选择:当比较规则不会轻易发生改变的时候,或者说当比较规则只有一个的时候,建议实现Comparable接口。如果比较规则有多个,并且需要多个比较规则之间的频繁切换,建议使用Comparator接口
Collections工具类
List<String> l =new ArrayList();
l.add("abc");
l.add("xyz");
l.add("bcd");
//排序:
//注意:对list集合排序 ,需要保证list集合元素实现了:Comparable接口
Collections.sort(l);
for (String s : l) {
System.out.println(s);
}
大总结
-
集合这块主要需要掌握什么内容?
-
每个集合对象的创建(new)
-
向集合中添加元素
-
从集合中取出某个元素
-
遍历集合
-
主要的集合类:
ArrayList★1234
LinkedList★
HashSet(HashMap的key,存储在HashMap集合的元素需要同时重写hashCode+equals,没有下标,不能通过集合取)
TreeSet
HashMap
Properties
TreeMap
-