Map集合概述和特点
-
将键映射到值的对象
一个映射不能包含重复的键
Map:双列集合,一个键只能映射一个值,键是唯一的,键相同,值覆盖。 -
某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。
-
Map接口中的内部类:
public static interface Map.Entry<K,V>映射项(键-值对)
:
Map.entrySet 方法返回映射的 collection 视图,该方法返回的是的对象能查看每个结点对象的键与值;后面在遇到集合遍历的时候可以很好的理解。 -
Map集合的方法
-
Map接口的常见的实现类:
HashMap、TreeMap、Hashtable、LinkedHashMap
HashMap类
HashMap 他的键的数据结构是哈希表,键无序,且唯一。并允许使用 null 值和 null 键。线程不安全,效率高。
1、 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
2、 而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
保证键的唯一性:
3.Map类的集合只与键有关,我们可以通过添加元素的方法查看底层的代码;它的底层是调用键对象的equals()和hashCode()方法来保证键的唯一性;与hashSet集合一样,重写hashCode()是为了减少碰撞。由于键是存在哈希表中,为了保证键的唯一性,如果键的类型时自定义引用数据类型(例如Student对象),每一个new的对象地址值不同,那么就会被当做是不同的对象在哈希表均会存储;因此就需要重写Student类的hashcode与equals方法,重写的hashcode根据对象的成员变量值来获取哈希表的地址值,重写的equals方法比较字符串字面值而不是地址值,就可以筛选出两个地址值不同但是成员变量相同的对象,避免重复存储。
HashMap的方法:
举例理解:
public class test {
public static void main(String[] args) {
HashMap<String, String> hm = new HashMap<>();
//当键相同,值就会覆盖,返回的是上一次这个键所映射的那个旧值。
String s = hm.put("1", "11");
String s1 = hm.put("1", "12");
String s2 = hm.put("2", "13");
String s3 = hm.put("3", "13");
String s4 = hm.put("4", "13");
String s5 = hm.put("5", "13");
String s66 = hm.put(null, null);
System.out.println(hm);//{null=null, 1=12, 2=13, 3=13, 4=13, 5=13}
//当键相同,值就会覆盖,返回的是上一次这个键所映射的那个旧值。
System.out.println(s);//null
System.out.println(s1);//11
System.out.println(s2);//null
//清空集合
//hm.clear();
//获取集合的长度
System.out.println(hm.size());//6
//删除一个键,返回的是那个键所对应的值。
String s6 = hm.remove("1");
System.out.println(hm);//{null=null, 2=13, 3=13, 4=13, 5=13}
System.out.println(s6);//12
//传入key与value进行删除 返回布尔值
boolean remove = hm.remove("2", "13");
System.out.println(remove); //true
//判断集合是否包含键"1"
boolean b = hm.containsKey("1");
//判断集合是否包含值
boolean b1 = hm.containsValue("13");
//判断集合是否为空isEmpty()
boolean b3 = hm.isEmpty();
//通过键来获取值,没有返回null
String s7 = hm.get("2");
//通过键来获取值,没有对应的值,可以给一个默认值。
System.out.println(hm.getOrDefault("1", "no value"));//no value
//values();获取所有值的集合
Collection<String> values = hm.values();
System.out.println(values);//[null,13, 13, 13]
System.out.println("============================");
// Map 集合的遍历
//第一种:通过键找值的方式来遍历 keySet();获取所有键的集合
Set<String> keySet = hm.keySet();
System.out.println(keySet);//[null,3, 4, 5]
for (String key : keySet) {
String value = hm.get(key);
System.out.println(key + "==" + value);
}
/*null==null
3==13
4==13
5==13*/
//第二种:把键值对,看做一个对象,把所有键值对,对象获取出来,然后使用键值对,对象中的方法来获取键和值。
Set<Map.Entry<String, String>> entries1 = hm.entrySet();
/* Map.Entry<String, String>
K getKey ()
返回与此项对应的键。
V getValue ()
返回与此项对应的值。*/
for (Map.Entry<String, String> stringStringEntry : entries1) {
String key = stringStringEntry.getKey();
String value = stringStringEntry.getValue();
System.out.println(key + "==" + value);
}
/*
null==null
3==13
4==13
5==13*/
//第三种:JDK1.8 提供的遍历方式forEach()方法 采用匿名内部类的方式重写方法来遍历集合
hm.forEach(new BiConsumer<String, String>() {
@Override
//传入的就是hm的键与值
public void accept(String key, String value) {
System.out.println(key + "==" + value);
}
});
/*
null==null
3==13
4==13
5==13*/
LinkedHashMap类
键的数据结构链表和哈希表。链表保证了键有序,哈希表保证了键唯一。
LinkedHashMap<String, String> stringStringLinkedHashMap = new LinkedHashMap<>();
stringStringLinkedHashMap.put("1", "100");
stringStringLinkedHashMap.put("6", "100");
stringStringLinkedHashMap.put("3", "100");
stringStringLinkedHashMap.put("2", "100");
stringStringLinkedHashMap.put("41", "100");
stringStringLinkedHashMap.put("4", "100");
System.out.println(stringStringLinkedHashMap);//{1=100, 6=100, 3=100, 2=100, 41=100, 4=100}
TreeMap类
TreeMap:
键的数据结构是红黑树, 可保证键的排序和唯一性,线程是不安全的效率比较高
// 排序分为自然排序和比较器排序
//空参构造,就使用的自然排序。
//自然排序,他对键的要求是,要求键实现Comparable接口,重写 compareTo方法,根据此方法的返回值的正负0 来决定键的排列顺序
//自然排序
//Integer类型的键 默认实现了Comparable接口,因此不需要手动重写就可以对键排序
TreeMap<Integer, String> integerStringTreeMap = new TreeMap<>();
integerStringTreeMap.put(10, "a");
integerStringTreeMap.put(11, "a");
integerStringTreeMap.put(14, "a");
integerStringTreeMap.put(70, "a");
System.out.println(integerStringTreeMap);//{10=a, 11=a, 14=a, 70=a}
//自定义一个学生类,需要重写Comparable接口,才可以对于学生对象进行排序
//重写的方法是按照学生姓名长度来排序:
TreeMap<Student, String> hm1 = new TreeMap<>();
hm1.put(new Student("张三", 23), "s002");
hm1.put(new Student("李四", 24), "s003");
hm1.put(new Student("王五", 25), "s004");
hm1.put(new Student("赵六", 20), "s005");
System.out.println(hm1);
//{Student{name='张三', age=23}=s002, Student{name='李四', age=24}=s003, Student{name='王五', age=25}=s004, Student{name='赵六', age=20}=s005}
//使用比较器排序,根据年龄大小排序
TreeMap<Student, String> hm3 = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student a, Student b) {
int num = a.getAge() - b.getAge();
int num1 = num == 0 ? a.getName().compareTo(b.getName()) : num;
return num1;
}
});
hm3.put(new Student("张三", 23), "s002");
hm3.put(new Student("李四", 24), "s003");
hm3.put(new Student("王五", 25), "s004");
hm3.put(new Student("赵六", 20), "s005");
System.out.println(hm3);
//{Student{name='赵六', age=20}=s005, Student{name='张三', age=23}=s002, Student{name='李四', age=24}=s003, Student{name='王五', age=25}=s004}
Hashtable类
不允许存储null值和null键 线程安全效率低
Hashtable<Object, Object> objectObjectHashtable = new Hashtable<>();
// objectObjectHashtable.put(null,"abc");不允许键或者值 为 null
集合的嵌套练习
//练习:
/* 统计字符串中每个字符出现的次数:“aababcabcdabcde”,获取字符串中每一个字母出现的次数要求结果:
a(5)b(4)c(3)d(2)e(1)*/
//分析:使用键值对集合存储,键是字符,值是次数
String str = "aababcabcdabcde";
HashMap<Character, Integer> characterIntegerHashMap = new HashMap<>();
//遍历字符串中的元素,如果没有遍历的字符就存入,并先设置次数为1;如果已经存在遍历的元素,就在value值的基础上加1,并重新存储覆盖原key值。
for (int i = 0; i < str.length(); i++) {
if (!characterIntegerHashMap.containsKey(str.charAt(i))) {
characterIntegerHashMap.put(str.charAt(i), 1);
} else {
Integer integer = characterIntegerHashMap.get(str.charAt(i));
integer++;
characterIntegerHashMap.put(str.charAt(i), integer);
}
}
Set<Map.Entry<Character, Integer>> entries = characterIntegerHashMap.entrySet();
for (Map.Entry<Character, Integer> entry : entries) {
System.out.print(entry.getKey() + "(" + entry.getValue() + ")");
}
//a(5)b(4)c(3)d(2)e(1)
/*
基础班
张三 20
李四 22
就业班
王五 21
赵六 23
集合嵌套之HashMap嵌套HashMap
*/
HashMap<String, Integer> jcMap = new HashMap<>();
jcMap.put("张三",20);
jcMap.put("李四", 22);
HashMap<String, Integer> jyMap = new HashMap<>();
jyMap.put("王五", 21);
jyMap.put("赵六", 23);
HashMap<String, HashMap<String, Integer>> maxMap = new HashMap<>();
maxMap.put("基础班",jcMap);
maxMap.put("就业班",jyMap);
//遍历集合
Set<Map.Entry<String, HashMap<String, Integer>>> entries2 = maxMap.entrySet();
for (Map.Entry<String, HashMap<String, Integer>> stringHashMapEntry : entries2) {
System.out.println(stringHashMapEntry.getKey());
Set<Map.Entry<String, Integer>> entries3 = stringHashMapEntry.getValue().entrySet();
for (Map.Entry<String, Integer> stringIntegerEntry : entries3) {
System.out.println("\t"+stringIntegerEntry.getKey() + " " + stringIntegerEntry.getValue());
}
System.out.println();
}
/*
三国演义
吕布
周瑜
笑傲江湖
令狐冲
林平之
神雕侠侣
郭靖
杨过
Map 集合 嵌套List集合
* */
ArrayList<String> sgList = new ArrayList<>();
sgList.add("吕布");
sgList.add("周瑜");
ArrayList<String> xaList = new ArrayList<>();
xaList.add("令狐冲");
xaList.add("林平之");
ArrayList<String> sdList = new ArrayList<>();
sdList.add("郭靖");
sdList.add("杨过");
HashMap<String, ArrayList<String>> bigMap = new HashMap<>();
bigMap.put("三国演义",sgList);
bigMap.put("笑傲江湖", xaList);
bigMap.put("神雕侠侣", sdList);
Set<Map.Entry<String, ArrayList<String>>> entries3 = bigMap.entrySet();
for (Map.Entry<String, ArrayList<String>> stringArrayListEntry : entries3) {
System.out.println(stringArrayListEntry.getKey());
ArrayList<String> value = stringArrayListEntry.getValue();
for (String s8 : value) {
System.out.println(s8);
}
System.out.println();
}
/* A:
案例演示
集合嵌套之ArrayList嵌套HashMap
假设ArrayList集合的元素是HashMap。有3个。
每一个HashMap集合的键和值都是字符串。
周瑜-- - 小乔
吕布-- - 貂蝉
郭靖-- - 黄蓉
杨过-- - 小龙女
令狐冲-- - 任盈盈
林平之-- - 岳灵珊*/
HashMap<String, String> sgMap = new HashMap<>();
sgMap.put("周瑜","小乔");
sgMap.put("吕布", "貂蝉");
HashMap<String, String> sdMap = new HashMap<>();
sdMap.put("郭靖", "黄蓉");
sdMap.put("杨过", "小龙女");
HashMap<String, String> xaMap = new HashMap<>();
xaMap.put("令狐冲", "任盈盈");
xaMap.put("林平之", "岳灵珊");
ArrayList<HashMap<String, String>> bigList = new ArrayList<>();
bigList.add(sgMap);
bigList.add(sdMap);
bigList.add(xaMap);
//遍历集合
for (HashMap<String, String> stringStringHashMap : bigList) {
Set<Map.Entry<String, String>> entries4 = stringStringHashMap.entrySet();
for (Map.Entry<String, String> stringStringEntry : entries4) {
System.out.println(stringStringEntry.getKey() + "---" + stringStringEntry.getValue());
}
System.out.println();
}
工具类Collections
Java针对 Collection 集合,提供了一个工具类Collections 为了方便的去操作 Collection 集合
Collections成员方法
public static void sort (List < T > list):排序, 默认按照自然顺序
public static int binarySearch (List < ? > list, T key):二分查找 前提 元素有序
public static T max(Collection < ? > coll):获取最大值
public static T min(Collection < ? > coll):获取最小值
public static void reverse (List < ? > list):反转
public static void shuffle (List < ? > list):随机置换
ArrayList<Integer> list = new ArrayList<>();
list.add(20);
list.add(100);
list.add(203);
list.add(20333);
list.add(2014);
Collections.sort(list);
System.out.println(list);//[20, 100, 203, 2014, 20333]
//可以重写排序比较器 自定义排序方式
/*Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return 0;
}
});*/
System.out.println(Collections.binarySearch(list, 200000));
System.out.println(Collections.max(list));//20333
System.out.println(Collections.min(list));//20
Collections.reverse(list);
Collections.shuffle(list);
System.out.println(list);//[20333, 20, 100, 2014, 203]
//先创建一个集合充当牌盒子
ArrayList<String> pokerBox = new ArrayList<>();
//生成54张牌放进牌盒子
String[] colors={"♠","♥","♦","♣"};
String[] nums={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
for (String color : colors) {
for (String num : nums) {
String poker = color.concat(num);
pokerBox.add(poker);
}
}
//手动添加大小王
pokerBox.add("☀");
pokerBox.add("☼");
//洗牌
Collections.shuffle(pokerBox);
//发牌:得有三个人来斗地主,还得留三张底牌
ArrayList<String> 星仔 = new ArrayList<>();
ArrayList<String> 刀仔 = new ArrayList<>();
ArrayList<String> 高进 = new ArrayList<>();
ArrayList<String> 底牌 = new ArrayList<>();
//发牌方式1: 一人数个10来张发给你
/*List<String> strings = pokerBox.subList(0, 11);
for (String string : strings) {
星仔.add(string);
}*/
// 方式2:传统发牌 一人一张转着发
//用牌的索引对3取余
/* 星仔 0 3 6 9 余数 0
刀仔 1 4 7 10 余数 1
高进 2 5 8 11 余数 2 */
for (int i = 0; i < pokerBox.size(); i++) {
if(i>=pokerBox.size()-3){
底牌.add(pokerBox.get(i));
}else if(i%3==0){
星仔.add(pokerBox.get(i));
} else if (i % 3 == 1) {
刀仔.add(pokerBox.get(i));
}else{
高进.add(pokerBox.get(i));
}
}
//看牌
System.out.println(星仔);
斗地主排序版,牌发好之后,从小到大排好
/*分析思路:
* 使用HashMap存储牌与对应的索引,此时牌的顺序已经存储完成
* 将索引放入一个ArrayList中,并打乱顺序发给三个人
* 三个人使用TreeSet存储索引,完成自动排序
* */
HashMap<Integer, String> box = new HashMap<>();
ArrayList<String> poker = new ArrayList<>();
//生成54张牌放进牌盒子
String[] colorss={"♠","♥","♦","♣"};
String[] numss={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
for (String numss1 : numss) {
for (String colorss1 : colorss) {
String concat = colorss1.concat(numss1);
poker.add(concat);
}
}
//手动添加大小王
poker.add("☀");
poker.add("☼");
//生成索引与牌的Map集合
for (int i = 0; i < 54; i++) {
box.put(i,poker.get(i));
}
//生成三个TreeSet 接收索引
TreeSet<Integer> one = new TreeSet<>();
TreeSet<Integer> two = new TreeSet<>();
TreeSet<Integer> three = new TreeSet<>();
TreeSet<Integer> four = new TreeSet<>();
ArrayList<TreeSet<Integer>> treeSets = new ArrayList<>();
treeSets.add(four);
treeSets.add(one);
treeSets.add(two);
treeSets.add(three);
//将索引放入一个集合中
ArrayList<Integer> integers = new ArrayList<>();
for (int i = 0; i < 54; i++) {
integers.add(i);
}
//洗牌
Collections.shuffle(integers);
//根据索引发牌
for (Integer integer : integers) {
if (integer>= 51) {
four.add(integers.get(integer));
} else if (integer % 3 == 0) {
one.add(integers.get(integer));
} else if (integer % 3 == 1) {
two.add(integers.get(integer));
} else {
three.add(integers.get(integer));
}
}
//根据索引查看对应的牌
for (TreeSet<Integer> treeSet : treeSets) {
for (Integer integer : treeSet) {
String s8 = box.get(integer);
System.out.print(s8+" ");
}
System.out.println();
}
}
}