1.Map接口常用方法
Interface Map<K,V>
java.util.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( bject key)判断Map中是否包含某个key
boolean containsValue( Object value)判断Map中是否包含某个 value
boolean isEmpty()判断Map集合中元素个数是否为0
Set keySet()获Map集合所有的key(所有的键是一个Set集合)
V remove( Object key) 通边key删键值对
int size()获取Map集合中键值对的个数。
Collection values() 获动Map集合中所有的value,返回一个 Collection
Set<Map.Entry<K,V>> entrySet() 将Map集合转换为Set集合
没看到的是map中的静态内部类
一.静态内部类(了解entrySet()方法)
Set<Map.Entry<K,V>> entrySet()
返回一个 Set视图的映射包含在这个Map。
import java.util.HashSet;
import java.util.Set;
public class MyClass{
public static void main(String[] args) {
//类名叫做MyClass.InnerClass
MyClass.InnerClass.m1();
//创建静态内部类对象
MyClass.InnerClass mi=new MyClass.InnerClass();
mi.m2();
//给一个Set集合
//该Set集合中存的对象是: Myclass.Innerclass类型
Set<MyClass.InnerClass> set=new HashSet<>();
//这个Set集合存储的是字符串对象
Set<String> set2=new HashSet<>();
Set<MyMap.MyEntry<Integer,String>> set3=new HashSet<>();
}
static class InnerClass{
public static void m1(){
System.out.println("静态内部类m1方法");
}
public void m2(){
System.out.println("静态内部类的实例方法执行");
}
}
}
class MyMap{
public static class MyEntry<K,V>{
}
}
静态内部类m1方法
静态内部类的实例方法执行
二.Map接口常用方法
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class MapTest01 {
public static void main(String[] args) {
//创建Map集合对象
Map<Integer,String> map=new HashMap<>();
//向Map集合添加键值对
map.put(1,"lupeng"); //在这里进行了自动装箱
map.put(2,"zhangsan");
map.put(3,"zhangyinying");
map.put(4,"wangcan");
//通过key获取value
System.out.println(map.get(3));
//获取键值对的数量
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(2));
//判断是否包含某个value
System.out.println(map.containsValue("zhangyinying"));
//获取所有的value
System.out.println("+++++++++++++");
Collection<String> values=map.values();
for (String data:values){
System.out.println(data);
}
//清空Map集合
map.clear();
//判断是否为空
System.out.println(map.isEmpty());
}
}
zhangyinying
4
3
false
true
+++++++++++++
lupeng
zhangyinying
wangcan
true
三.遍历Map集合
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashSet01{
public static void main(String[] args) {
//创建Map集合对象
Map<Integer,String> map=new HashMap<>();
//向Map集合添加键值对
map.put(1,"zhansan");//使用自动装箱
map.put(2,"yinying");
map.put(3,"lupeng");
map.put(4,"wangcan");
//重复便覆盖HashMap
map.put(1,"huyiwen");
//Set<Integer> set=map.keySet();//获取Map集合所有的key(所有的键是一个Set集合)
// Iterator<Integer> it= set.iterator();
/* while (it.hasNext()){
Integer key=it.next();
System.out.println(key+"="+map.get(key));
}*/
/* for (Integer key:set){
System.out.println(key+"="+map.get(key));
}*///上述效率较低,通过key获取value
//第二种方式:Set<Map.Entry<K,V>> entrySet()
//以上这个方法是把Map集台接全部转换成Set集合。
//Set集合中元素的类型是: Map.Entry
Set<Map.Entry<Integer,String>> set=map.entrySet();
Iterator<Map.Entry<Integer,String>> iterator2=set.iterator();
while(iterator2.hasNext()){
Map.Entry<Integer,String> node= iterator2.next();
Integer key=node.getKey();
String value=node.getValue();
System.out.println(key+"="+value);
}
for(Map.Entry<Integer,String> node:set){
System.out.println(node.getKey()+"---"+node.getValue());
}
}
}
1=huyiwen
2=yinying
3=lupeng
4=wangcan
1—huyiwen
2—yinying
3—lupeng
4—wangcan
Map集合转换成Set集合entrySet()方法
1.哈希表数据结构
HashMap集合:
1、 HashMap集合底层是哈希表/散列表的数据结构。
2、哈希表是一个怎样的数据结构呢?
哈希表是一个数组和单向链表的结合体。
数组:在查询方面效率很高,随机增删方面效率很低。
单向链表:在随机增删方面效率较高,在查询方面效率很低
哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。
HashMap集合底层的源代码:
public class HashMap{
// HashMap底层实际上就是一个数组。(一维数组)
Node<k, v> table;
//静态的内部类HashMan.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部分的元素其实就是放到HashSet集合中了。
所以Hashset集合中的元素也需要同时重写hashCode()+ equals()方法。
6、哈希表 HashMap使用不当时无法发挥性能!
假设将所有的 hashCode()方法返回值固定为某个值,那么会导致底层哈带表变成了纯单向链表。这种情况我们成为:散列分布不均匀。
什么是散列分布均匀?
假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的是散列分布均匀的。
假设将所有的 hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了也是散列分布不均匀,散列分布均匀需要你重写hashCode()方法时有一定的技巧。
7、重点:放HashMp集合key部分的元素,似及放在Hashset集合中的元素,需要同时重马 hashcode和 equals方法。
8、 HashMap集台的默认初始化容量是16,默认加载因子是0.75
这个默认加载因子是当 HashMap集合底层数组的容量达到5%的时候,数组开始扩容。
重点,记住:阳shM集合初始化容量必须是2的倍数,这也是宫方推荐的
这是因为达到散列均匀,为了提高 HashMap集合的存取效率,所必须的
哈希表表或散列表数据结构
2.用户自定义对象需重写hashcode和equals方法(同时对于HashSet也是一样,底层是HashMap)
Java 自定义对象作为 Map 的 key 时,需重写 equals() 和 hashCode() 方法
//之前在key中String和Integer类都重写了equals,所以不需要重写equals和hashcode
import java.util.HashSet;
import java.util.Set;
/*
1.向Map集合中存,以及从Map集合中取,都是先调用key的HashCode方法,然后再调用equals方法!
equals方法有可能调用,也有可能不调用。
拿put(k,V)举例,什么时候 equals不会调用?
k.hashcode()方法返回哈希值,
哈希值经过哈希算法转换成数组下标
数组下标位置上如果是null, equals不需要执行。
拿get(k)举例,什么时候 equals不会调用?
k. hashcode()方法返回哈带值,
哈带值经过哈带算法转换成数组下标。
数组下标位置上如果是nuLL, equals不需要执行。
2.注意:如果一个类的 equals方法重写了,那么 hashCode()方法必须重写。
并且 equals方法返回如果是true, hashcode()方法返回的值必须一样。
equals方法返回true表示两个对象相同,在同一个单向链表上比较。
那么对于同一个单向链表上的节点来说,他们的哈希值部是相同的
所以hashcode()方法的返回值也应该相同。
3.hashcode()方法和equals()方法不用研究了,接使用IDEA工具生成,但是这两个方法需要同时生成。
4.终极结论:
放在HashMap集合Key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。
5.对于哈希表数据结构来说
如果01和02的hash值相同,一定是放到同一个单向链表上。
当然如果01和2的hash值不同,但由于哈带算法执行结束之后转换的数组下标可能相同,此时会发生哈希碰撞。*/
public class Student {
// 姓名
private String stuNm;
// 年龄
private String age;
// 构造函数
public Student(String stuNm, String age) {
this.stuNm = stuNm;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
if (stuNm != null ? !stuNm.equals(student.stuNm) : student.stuNm != null) return false;
return age != null ? age.equals(student.age) : student.age == null;
}
@Override
public int hashCode() {
int result = stuNm != null ? stuNm.hashCode() : 0;
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
}
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<Student, String> map = new HashMap<>();
Student stuA = new Student("张三", "20");
map.put(stuA, "95");
Student stuB = new Student("张三", "20");
map.put(stuB, "93");
System.out.println(map.get(stuA)); // 93
System.out.println(map.get(stuB)); // 93
System.out.println("stuA的hashcode是"+stuA.hashCode());
System.out.println("stuB的hashcode是"+stuB.hashCode());
Student stuC = new Student("张三", "20");
System.out.println(map.get(stuC)); // 93
System.out.println(stuA.equals(stuC)); // true
}
}
93
93
stuA的hashcode是24023157
stuB的hashcode是24023157
93
true
3.HashMap和HashTable的区别
Hashtable的key可以为nuL吗?
Hashtable的key和aLue都是不能为null的。
HashMap集台的key和value都是可以为nuLL的
Hashtable方法都带有 synchronized:线程安全的。
线程安全有其它的方案,这个 Hashtable对线程的处理
导致效率较低,使用较少.
import java.util.Hashtable;
import java.util.Map;
public class HashtableTest01 {
public static void main(String[] args) {
Map map=new Hashtable();
//map.put(null,11);
map.put(100,null);
}
}
4.Properties类
目前只需要掌握Properties属性类对象的相关方法即可
Properties是一Map集合,继Hashtable, Properties的key和value都是String类型。
Properties被称为属性类对象。
Properties是线程安全的
import java.util.Properties;
public class PropertiesTest01 {
public static void main(String[] args) {
//创建一个Properties对象
Properties pro=new Properties();
//需要掌握Properties的两个方法,一个存、一个取
pro.setProperty("lupeng1","root123");
pro.setProperty("lupeng2","root12353");
pro.setProperty("lupeng3","root12433");
pro.setProperty("lupeng4","root45123");
//通过key获取value
String username1=pro.getProperty("lupeng1");
String username2=pro.getProperty("lupeng2");
String username3=pro.getProperty("lupeng3");
String username4=pro.getProperty("lupefsng4");
System.out.println(username1);
System.out.println(username2);
System.out.println(username3);
System.out.println(username4);
}
}
root123
root12353
root12433
null
二.TreeMap集合
Java异常ClassCastException
1.TreeSet无法对自定义类型排序
对自定义的类型来说, Treeset可以排序吗?
以下程序中对于 Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。
谁大谁小并没有说明啊。
以下程序运行的时候出现了这个异常
java.lang.ClassCastException;
class com.javase.collection.HashSet.Person cannot be cast to class java.lang.Comparable
出现这个异常的原因
Person:类没有实现java.lang. Comparable接口
2.TreeMap用户自定义比较(两种方式)
1.用户自定义类型实现Comparable接口
/放在 Treeset集合中的元素需要实现iava.lang. Comparable口
//并且实现 compareTo方法。 equals可以不写。
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest05 {
public static void main(String[] args) {
TreeSet<Vip> vips=new TreeSet<>();
vips.add(new Vip("zhangsan",20)); //添加指定元素到集合中
vips.add(new Vip("zhangyinying",20));
vips.add(new Vip("lupeng",19));
vips.add(new Vip("wangcan",20));
//使用迭代器
Iterator it=vips.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
vips.add(new Vip("fuck",19));
System.out.println("+++++++++++++++++++");
//增强for
for (Vip vip:
vips) {
System.out.println(vip);
}
}
}
class Vip implements Comparable<Vip>{
String name;
int age;
public Vip() {
}
public Vip(String name, int age) {
this.name = name;
this.age = age;
}
/* 需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
k.compareTo(t key)
拿着参数和集合中的每一个进行比较,返回值可能是>0 <0 =0*/
@Override
public int compareTo(Vip v) {
if (this.age==v.age){
return this.name.compareTo(v.name);
}else {
return this.age-v.age;
}
}
@Override
public String toString() {
return "Vip{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Vip{name=‘lupeng’, age=19}
Vip{name=‘wangcan’, age=20}
Vip{name=‘zhangsan’, age=20}
Vip{name=‘zhangyinying’, age=20}
+++++++++++++++++++
Vip{name=‘fuck’, age=19}
Vip{name=‘lupeng’, age=19}
Vip{name=‘wangcan’, age=20}
Vip{name=‘zhangsan’, age=20}
Vip{name=‘zhangyinying’, age=20}
平衡二叉树数据结构
2.实现比较器接口
TreeSet集合中元素可排序的第二种方式:使用比较器的方式
最终的结论:
放到 Treeset-或者TreeMap集合key部分的元素要想做到排序包括两种方式:
第一种:放在集合中的元素实现java.lang. Comparable接口。
第二种:在构造TreeSet或者 TreeMap集合的时候给它传一个比较器对象。
Comparable和 Comparator怎么选择呢?
当比较规则不会发生改交的时候,或者说当比较规则只有1个的时候,建议实现 comparable接口。
如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用 Comparator接口。
Comparator接口的设计符台OCP原则。
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest06 {
public static void main(String[] args) {
//实现接口compare
TreeSet<Wugai> wugais=new TreeSet<>(new WuGaiComparetor());
//采用匿名内部类
/* TreeSet<Wugai> wugais = new TreeSet<>(new Comparator<Wugai>() {
@Override
public int compare(Wugai o1, Wugai o2) {
return o1.age - o2.age;
}
});*/
wugais.add(new Wugai(423));
wugais.add(new Wugai(234));
wugais.add(new Wugai(35));
for (Wugai wugai :
wugais) {
System.out.println(wugai);
}
}
}
class Wugai {
int age;
public Wugai(int age) {
this.age = age;
}
@Override
public String toString() {
return "Wugai{" +
"age=" + age +
'}';
}
}
//单独在这里编写了一个比较器
//比较器实现java.util.Comparator接口.(Comparable是java.lang包下的。Comparator是java.util包下的)
class WuGaiComparetor implements Comparator<Wugai>{
@Override
public int compare(Wugai o1, Wugai o2) {
return o1.age-o2.age;
}
}
Wugai{age=35}
Wugai{age=234}
Wugai{age=423}
3.Collections工具类
import java.util.*;
public class CollectionTest {
public static void main(String[] args) {
//ArrayList集合不是线程安全的
List<String> list=new ArrayList<>();
//变成线程安全的
Collections.synchronizedList(list);
list.add("aba");
list.add("aba");
list.add("abd");
list.add("abx");
list.add("abc");
Collections.sort(list);
for (String data:
list) {
System.out.println(data);
}
Set<String> set=new HashSet<>();
set.add("lupeng2");
set.add("lupeng4");
set.add("lupeng1");
set.add("lupeng3");
//将set集合转换为list集合
List<String> myList=new ArrayList<>(set);
//可以使set集合变成list集合进行排序
Collections.sort(myList);
for (String s:myList){
System.out.println(s);
}
}
}
class Wugui{
int age;
public Wugui(int age) {
this.age = age;
}
}
aba
aba
abc
abd
abx
lupeng1
lupeng2
lupeng3
lupeng4