Map集合
键映射到值的对象,Map集合可以多个值,但键必须唯一!
Map和Collection集合的区别:
Collection:只能存储一种类型 Collection
Map集合:可以两种类型的,键的类型,值的类型 Map<K,V>
遍历方式不同
Collection:5种方式
详见集合的遍历
Map:两种方式:
方式1:获取所有的K的集合(键的集合)
通过键获取值方式2: 获取所有的键值对对象Map.Entry<K,V> (“结婚证”)
通过键值对对象获取所有的键(“结婚证男方”)
通过键值对对象获取所有的值(“结婚证女方”)
有内在联系:
TreeSet集合:Collection---->间接的使用到了TreeMap集合的put方法
HashSet阶:Collection---->间接使用到了HashMap的put方法
Map集合的功能:
V put(K key,V value);//添加键值对元素
/*注意事项:
如果key是第一次添加,那么返回的结果为null
如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值
*/
V remove(Object key);//删除指定的键,返回被删除键对应的值
void clear()
boolean containsKey(Object key);//是否包含指定的键 (使用居多)
boolean containsValue(Object value);//是否包含指定的值
高级功能(遍历):
方法1:
Set<K> keySet();//获取当前Map集合中的所有的键的集合 (将所有的丈夫集中起来,找对应的妻子)
V get(Object key);//通过键获取值
方式2:
//获取所有的结婚证 (键值对对象)
Set<Map.Entry<K,V>> entrySet();
//通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
K getKey();
V getValue();
eg.
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
//创建Map集合对象
Map<String, String> map = new HashMap<>();
//添加元素
map.put("令狐冲", "东方不败");
map.put("杨过", "小龙女");
map.put("陈玄风", "梅超风");
map.put("郭靖", "黄蓉");
//方法1:
// Set<K> keySet() :获取当前Map集合中的所有的键的集合
Set<String> keySet = map.keySet();
//增强for遍历
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + "-" + value);
}
System.out.println("————————————————");
//方式2:
//Set<Map.Entry<K,V>> entrySet()
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//增强for:遍历键值对对象获取到
for (Map.Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey()+"-"+entry.getValue());
}
}
}
运行结果:
令狐冲-东方不败
陈玄风-梅超风
杨过-小龙女
郭靖-黄蓉
————————————————
令狐冲-东方不败
陈玄风-梅超风
杨过-小龙女
郭靖-黄蓉
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("url", "jdbc:mysql://localhost:3306/bjpowernode");
pro.setProperty("driver","com.mysql.jdbc.Driver");
pro.setProperty("username", "root");
pro.setProperty("password", "123");
// 通过key获取value
String url = pro.getProperty("url");
String driver = pro.getProperty("driver");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println(url);
System.out.println(driver);
System.out.println(username);
System.out.println(password);
}
}
Collections工具类
针对集合操作工具类
提供静态功能
public static <T extends Comparable<? super T>> void sort(List<T> list);//按照自然升序排序(针对List集合排序)
public static <T> void sort(List<T> list,Comparator<? super T> c);//按照比较器排序针对List集合
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>;//获取当前自然顺序中List的最大值
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>;//最小值
public static void reverse(List<?> list);//对List集合顺序反转
public static void shuffle(List<?> list);//随机置换
eg.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test01 {
public static void main(String[] args) {
//创建List集合
List<Integer> list = new ArrayList<>() ;
//添加元素
list.add(10) ;
list.add(50) ;
list.add(15) ;
list.add(25) ;
list.add(5) ;
list.add(12) ;
System.out.println(list);//[10, 50, 15, 25, 5, 12]
System.out.println("---------------------------------");
//排序
Collections.sort(list);
System.out.println(list);//[5, 10, 12, 15, 25, 50]
System.out.println("----------------------------------");
//最大值
Integer max = Collections.max(list);
System.out.println(max);//50
System.out.println("----------------------------------");
//最小值
System.out.println(Collections.min(list));//5
System.out.println("-----------------------------------");
//反转
Collections.reverse(list);
System.out.println(list);//[50, 25, 15, 12, 10, 5]
System.out.println("------------------------------------");
//乱序
Collections.shuffle(list);
System.out.println(list);//[10, 25, 15, 12, 50, 5]
}
}
集合排序
public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
List<Student>:针对List进行自然升序排序,
eg.
主要条件:按照学生的年龄从小到大排
次要条件:年龄相同,比较姓名内容是否相同!
public class CollectionsTest {
public static void main(String[] args) {
//创建List集合对象
List<Student> list = new ArrayList<>() ;
//添加
list.add(new Student("gaogaoyuan",42)) ;
list.add(new Student("gaogaoyuan",40)) ;
list.add(new Student("liushishi",42)) ;
list.add(new Student("wanglihong",45)) ;
list.add(new Student("wenzhang",38)) ;
list.add(new Student("huazi",35)) ;
list.add(new Student("huazi",32)) ;
list.add(new Student("zhangjunjie",20)) ;
//排序
//Collections.sort(list); //自然升序排序:针对集合当前存储的类型必须实现Comparable
//使用比较器排序:针对List集合
//public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge() ;
int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
return num2;
}
});
for(Student s:list){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
import java.util.Comparator;
//实现接口
//public class Student implements Comparable<Student> {
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 int compareTo(Student s) {
//主要条件:按照学生的年龄从小到大排序
int num = this.age - s.age ;
//如果年龄相同,姓名比(字典顺序)
int num2 = (num==0)?(this.name.compareTo(s.name)):num ;
return num2;
}*/
}
集合总结
ArrayList集合:
1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)
2、集合底层是一个Object[]数组。
3、构造方法:
new ArrayList();
new ArrayList(20);4、ArrayList集合的扩容:
增长到原容量的1.5倍。
ArrayList集合底层是数组,怎么优化?
尽可能少的扩容。因为数组扩容效率比较低,建议在使用ArrayList集合
的时候预估计元素的个数,给定一个初始化容量。5、数组优点:
检索效率比较高。(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,
然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。)6、数组缺点:
随机增删元素效率比较低。
另外数组无法存储大数据量。(很难找到一块非常巨大的连续的内存空间。)7、向数组末尾添加元素,效率很高,不受影响。
8、面试官经常问的一个问题?
这么多的集合中,你用哪个集合最多?
答:ArrayList集合。
因为往数组末尾添加元素,效率不受影响。
另外,我们检索/查找某个元素的操作比较多。9、ArrayList集合是非线程安全的。(不是线程安全的集合。)
泛型
1、JDK5.0之后推出的新特性:泛型
2、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)
3、使用了泛型好处是什么?
第一:集合中存储的元素类型统一了。
第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!4、泛型的缺点是什么?
导致集合中存储的元素缺乏多样性!
大多数业务中,集合中元素的类型还是统一的。所以这种泛型特性被大家所认可。
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)
以上这两个方法的实现原理,是必须掌握的。 map.put(k,v)的实现原理:
第一步:先将k,v封装到Node对象当中。
第二步:底层会调用k的hashCode0方法得出hash值,然后通过哈希函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上了。如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals ,如果所有的equals方法返回都是false ,那么这个新节点将会被添加到链表的末尾。如果其中有一个equals返回了true,那么这个节点的value将会被覆盖。 v = map.get(k)的实现原理:
先调用k的hashCode0方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么也没有,返回null.如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals ,如果所有equals方法返回false ,那么get方法返回null ,只要其中有一个节点的k和参数k equals的时候返回true ,那么此时这个节点的value就是我们要找的value , get方法最终返回这个要找的value。5、HashMap集合的key部分特点:
无序,不可重复。
为什么无序? 因为不一定挂到哪个单向链表上。
不可重复是怎么保证的? equals方法来保证HashMap集合的key不可重复。
如果key重复了,value会
放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
所以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集合的存取效率,所必须的。
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(Object key) 判断Map中是否包含某个key
boolean containsValue(Object value) 判断Map中是否包含某个value
boolean isEmpty() 判断Map集合中元素个数是否为0
V remove(Object key) 通过key删除键值对
int size() 获取Map集合中键值对的个数。
Collection values() 获取Map集合中所有的value,返回一个Collection Set keySet() 获取Map集合所有的key(所有的键是一个set集合)
HashMap集合补充
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、对于哈希表数据结构来说:
如果o1和o2的hash值相同,一定是放到同一个单向链表上。
当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。
扑克牌
eg.1
import java.util.ArrayList;
import java.util.Collections;
public class Test06 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
String[] colors = {"♥", "♦", "♠", "♣"};
String[] numbers = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
for (String number : numbers) {
for (String color : colors) {
arrayList.add(color + number);
}
}
arrayList.add("大王");
arrayList.add("小王");
//洗牌
Collections.shuffle(arrayList);
ArrayList<String> player1 = new ArrayList<>();
ArrayList<String> player2 = new ArrayList<>();
ArrayList<String> player3 = new ArrayList<>();
ArrayList<String> dipai = new ArrayList<>();
for (int i = 0; i < arrayList.size(); i++) {
if (i <arrayList.size()-3) {
if ((i) % 3 == 0) {
player1.add(arrayList.get(i));
} else if ((i) % 3 == 1) {
player2.add(arrayList.get(i));
} else{
player3.add(arrayList.get(i));
}
} else {
dipai.add(arrayList.get(i));
}
}
System.out.print("player1:");
Sort(player1);
System.out.print("player2:");
Sort(player2);
System.out.print("player3:");
Sort(player3);
System.out.print("dipai:");
Sort(dipai);
}
public static void Sort(ArrayList<String> arrayList){
for (String s:arrayList) {
System.out.print(s+"\t");
}
System.out.println();
}
}
运行结果:
player1:♥6 ♣10 ♣J ♦5 ♥2 ♠J ♠3 ♣7 ♣6 ♠8 ♠Q ♥9 ♣4 ♠5 ♦K ♥7 ♦7
player2:♥5 ♣8 ♣3 ♦4 ♦3 ♥10 ♦J ♥3 ♣A ♠K ♥4 ♦6 大王 ♠7 小王 ♦2 ♠A
player3:♦A ♣2 ♣Q ♣9 ♥A ♥8 ♣K ♠6 ♦Q ♦8 ♠2 ♠9 ♠10 ♦10 ♣5 ♥Q ♦9
dipai:♥K ♠4 ♥J
eg.2
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
public class Test06_2 {
public static void main(String[] args) {
HashMap<Integer,String> map = new HashMap<>();
ArrayList<Integer> list = new ArrayList<>();
String[] colors = {"♥", "♦", "♠", "♣"};
String[] numbers = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
int index = 0;
for (String number:numbers) {
for (String color:colors) {
map.put(index,color+number);
list.add(index);
index++;
}
}
list.add(index);
map.put(index++,"大王");
list.add(index);
map.put(index,"小王");
//洗牌
Collections.shuffle(list);
TreeSet<Integer> player1= new TreeSet<>();
TreeSet<Integer> player2= new TreeSet<>();
TreeSet<Integer> player3= new TreeSet<>();
TreeSet<Integer> dipai= new TreeSet<>();
for (int i = 0; i < list.size(); i++) {
if (i <list.size()-3) {
if ((i) % 3 == 0) {
player1.add(list .get(i));
} else if ((i) % 3 == 1) {
player2.add(list .get(i));
} else{
player3.add(list .get(i));
}
} else {
dipai.add(list .get(i));
}
}
Sort("palyer1",player1,map);
Sort("palyer2",player2,map);
Sort("palyer3",player3,map);
Sort("底牌",dipai,map);
}
public static void Sort(String name,TreeSet<Integer> set,HashMap<Integer,String> map ){
System.out.print(name+"的牌是:");
for (Integer i:set) {
System.out.print(map.get(i)+"\t");
}
System.out.println();
}
}
运行结果:
palyer1的牌是:♦2 ♠2 ♦3 ♠3 ♣3 ♦6 ♣6 ♥7 ♦7 ♥8 ♠9 ♦10 ♥Q ♦Q ♣Q ♦K ♠K
palyer2的牌是:♥A ♠A ♥3 ♠4 ♣4 ♦5 ♠5 ♣5 ♣7 ♦9 ♣9 ♠10 ♦J ♠J ♣J ♣K 大王
palyer3的牌是:♦A ♣A ♥2 ♣2 ♥4 ♦4 ♥5 ♥6 ♠7 ♠8 ♥9 ♥10 ♣10 ♥J ♠Q ♥K 小王
底牌的牌是:♠6 ♦8 ♣8