集合进阶-双列集合(十三) 双列集合特点 :
双列集合一次需存储一对数据,分别为键 和值
键 不能重复,值 可以重复
键 和值 是一一对应的,每个键只能找到自己对应的值
键 和值 这个整体称之为键值对 或键值对对象 ,在Java中叫做Entry对象
Map Map是双列集合的顶层接口,它的功能是全部双列集合都可继承使用的。
体系结构
Map(接口)
HashMap (实现类)->LinkedHashMap(实现类)
Hashtable(实现类)->Properties(实现类)
TreeMap(实现类)
常用方法 put 格式 :
V put(K key,V value)
说明 :添加元素。
在添加数据时,若键不存在,直接把键值对对象添加到map集合当中,方法返回是null
在添加数据时,若键存在,会把原有的键值对对象覆盖,并将被覆盖的值进行返回。
remove 格式 :
V remove(Object Key)
说明 :根据键删除键值对元素
clear 格式 :
void clear()
说明 :移除所有键值对元素
containsKey 格式 :
boolean containsKey
说明 :判断集合是否包含指定的键
containsValue 格式 :
boolean containsValue
说明 :判断集合是否包含指定的值
isEmpty 格式 :
boolean isEmpty()
说明 :判断集合是否为空
size 格式 :
int size()
说明 :集合的长度,也是集合中键值对的个数
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { Map<String,String> m=new HashMap<>(); System.out.println("put方法"); m.put("aaa","AAA"); m.put("bbb","BBB"); m.put("ccc","CCC"); System.out.println(m); String value1=m.put("aaa","sss"); System.out.println(m); System.out.println(value1); String value2=m.put("ddd","DDD"); System.out.println(value2); System.out.println(m); System.out.println("\nremove方法"); m.remove("bbb"); System.out.println(m); System.out.println("\ncontainsKey方法"); System.out.println(m.containsKey("aaa")); System.out.println(m.containsKey("bbb")); System.out.println("\ncontainsValue方法"); System.out.println(m.containsValue("DDD")); System.out.println(m.containsValue("AAA")); System.out.println("\nsize方法"); System.out.println(m.size()); System.out.println("\nisEmpty方法"); System.out.println(m.isEmpty()); System.out.println("\nclear方法"); m.clear(); System.out.println(m.isEmpty()); System.out.println(m); } }
遍历方式
键找值
键值对
Lambda表达式
键找值 利用keySet方法 将所有键放入一个单列集合中,遍历得到每一个键,通过get方法 得到每个键对应的值。
keySet方法 :
Set keySet()
get方法 :
V get(Object key)
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { Map<String,String> m=new HashMap<>(); m.put("aaa","AAA"); m.put("bbb","BBB"); m.put("ccc","CCC"); Set<String> set=m.keySet(); System.out.println("迭代器遍历"); Iterator<String> it=set.iterator(); while(it.hasNext()){ String str=it.next(); System.out.println(str+" = "+m.get(str)); } System.out.println("\n增强for"); for(String str:set){ System.out.println(str+" = "+m.get(str)); } System.out.println("\nlambda"); set.forEach(str-> System.out.println(str+" = "+m.get(str))); } } /* 迭代器遍历 aaa = AAA ccc = CCC bbb = BBB 增强for aaa = AAA ccc = CCC bbb = BBB lambda aaa = AAA ccc = CCC bbb = BBB */
键值对 通过entrySet 方法获取所有键值对对象,在通过键值对对象调用getkey 和getValue 获取键和值。
entrySet方法 :
Set<Map.Entry<K,V>> entrySet()
getkey方法 :
K getKey()
getValue方法 :
V getValue()
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { Map<String,Integer> map=new HashMap<>(); map.put("a",97); map.put("b",98); map.put("c",99); Set<Map.Entry<String,Integer>> entries=map.entrySet(); //若导入import java.util.Map.Entry; //则可以直接Set<Entry<String,Integer>> entries=map.entrySet(); System.out.println("迭代器"); Iterator<Map.Entry<String,Integer>> it=entries.iterator(); while(it.hasNext()){ Map.Entry<String,Integer> entry=it.next(); String key=entry.getKey(); int value=entry.getValue(); System.out.println(key+" = "+value); } System.out.println("\nfor增强"); for(Map.Entry<String,Integer> entry:entries){ String key=entry.getKey(); int value=entry.getValue(); System.out.println(key+" = "+value); } System.out.println("\nLambda"); entries.forEach(entry-> System.out.println(entry.getKey()+" = "+entry.getValue())); } } /* 迭代器 a = 97 b = 98 c = 99 for增强 a = 97 b = 98 c = 99 Lambda a = 97 b = 98 c = 99 */
Lambda表达式 方法名称 :
default void forEach(BiConsumer<? super K, ? super V> action)
说明 :结合Lambda遍历Map集合
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.util.HashMap; import java.util.Map; //import java.util.function.BiConsumer; public class Test{ public static void main(String[] args){ Map<String,Integer> map=new HashMap<>(); map.put("a",97); map.put("b",98); map.put("c",99); /* map.forEach(new BiConsumer<String, Integer>() { @Override public void accept(String s, Integer integer) { System.out.println(s+" = "+integer); } }); */ map.forEach((s,integer)-> System.out.println(s+" = "+integer)); } } /* a = 97 b = 98 c = 99 */
选择方式 默认 :HashMap(效率最高)
若要保证存取有序 ,LinkedHashMap
若要进行排序 :TreeMap
HashMap
HashMap是Map里的一个实现类,并无额外需要学习的特殊方法,直接使用Map的方法即可
特点是由键决定:无序,不重复,无索引
HashMap和HashSet底层原理一样,都是哈希表结构
依赖hashCode和equals方法保证键的唯一
若键存储的是自定义对象,需重写hashCode和equals方法;若值存储的是自定义对象,不需重写hashCode和equals方法。
范例一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 /* 创建一个HashMap集合,键是学生对象(Student),值是籍贯(String) 存储三个键值对元素,并遍历。 要求:同姓名,同年龄认为是同一个学生 */ //Student.java import java.util.Objects; public class Student{ private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } public String toString() { return "Student{name = " + name + ", age = " + age + "}"; } } //Test.java import java.util.HashMap; import java.util.Set; public class Test { public static void main(String[] args) { HashMap<Student,String> hm=new HashMap<>(); Student stu1=new Student("zs",18); Student stu2=new Student("lisi",19); Student stu3=new Student("zs",18); String s1="河北"; String s2="河南"; String s3="北京"; hm.put(stu1,s1); hm.put(stu2,s2); hm.put(stu3,s3); System.out.println("lambda"); hm.forEach((stu,s)-> System.out.println(stu+" = "+ s)); System.out.println("键找值"); Set<Student> set=hm.keySet(); for(Student stu:set){ System.out.println(stu +" = "+hm.get(stu)); } System.out.println("键值对"); Set<HashMap.Entry<Student,String>> entries=hm.entrySet(); for(HashMap.Entry<Student,String> entry:entries){ System.out.println(entry.getKey()+ " = "+entry.getValue()); } } } /* lambda Student{name = lisi, age = 19} = 河南 Student{name = zs, age = 18} = 北京 键找值 Student{name = lisi, age = 19} = 河南 Student{name = zs, age = 18} = 北京 键值对 Student{name = lisi, age = 19} = 河南 Student{name = zs, age = 18} = 北京 */
范例二: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 /* 某个班级80个学生,现需组成春游活动 班长提供四个景区A,B,C,D 每个学生只能选择一个景区,并统计出哪个景区想去的人数最多 */ package com.ljsblog.domain1; import java.util.ArrayList; import java.util.HashMap; import java.util.Random; import java.util.Set; public class Test { public static void main(String[] args) { String[] arr={"A","B","C","D"}; ArrayList<String> list=new ArrayList<>(); Random random=new Random(); for(int i=0;i<80;i++){ int index=random.nextInt(arr.length); list.add(arr[index]); } HashMap<String,Integer> hm=new HashMap<>(); for(String name:list){ if(hm.containsKey(name)){ int c=hm.get(name); c++; hm.put(name,c); }else{ hm.put(name,1); } } Set<HashMap.Entry<String,Integer>> entries=hm.entrySet(); int max=0; for(HashMap.Entry<String,Integer> entry:entries){ int value=entry.getValue(); if(max<value){ max=value; } } System.out.println(hm); System.out.println("最大为"+max); for(HashMap.Entry<String,Integer> entry:entries){ if(max==entry.getValue()){ System.out.println(entry.getKey()); } } } } /* {A=19, B=18, C=21, D=22} 最大为22 D */
LinkedHashMap 由键决定:有序 ,不重复 ,无索引
底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存取的顺序。
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.util.LinkedHashMap; public class Test { public static void main(String[] args) { LinkedHashMap<String ,Integer> lhm=new LinkedHashMap<>(); lhm.put("a",96); lhm.put("a",97); lhm.put("b",98); lhm.put("c",99); System.out.println(lhm); } } /* {a=97, b=98, c=99} */
TreeMap TreeMap跟TreeSet底层原理一样,都是红黑树结构的。
由键决定特性:不重复,无索引,可排序。
可排序:对键进行排序、
注 :默认按照键的从小到大进行排序,也可自己规定键的排序规则
代码书写的两种排序规则 :
实现Comparable接口,指定比较规则。
创建集合时传递Comparator比较器对象,指定比较规则
范例一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /* 键:整数表示id 值:字符串表示商品名称 要求:按照id的升序排序,按照id的降序排序 */ import java.util.TreeMap; public class Test { public static void main(String[] args) { //默认升序 TreeMap<Integer,String> tm1=new TreeMap<>(); tm1.put(6,"足球"); tm1.put(5,"篮球"); tm1.put(10,"橄榄球"); System.out.println(tm1); //降序 TreeMap<Integer,String> tm2=new TreeMap<>((o1, o2)->o2-o1); tm2.put(7,"足球"); tm2.put(6,"篮球"); tm2.put(10,"橄榄球"); System.out.println(tm2); } }
范例二 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 /* 键:学生对象(姓名和年龄) 值:籍贯 要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排序,同姓名通年龄视为同一人 */ //Student.java public class Student implements Comparable<Student>{ private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } public String toString() { return "Student{name = " + name + ", age = " + age + "}"; } @Override public int compareTo(Student o) { int i=this.getAge()-o.getAge(); return i==0?this.getName().compareTo(o.getName()):i; } } //Test.java import java.util.TreeMap; public class Test { public static void main(String[] args) { TreeMap<Student,String> tm=new TreeMap<>(); Student s1=new Student("zs",19); Student s2=new Student("ww",18); Student s3=new Student("zs",19); Student s4=new Student("ls",19); tm.put(s1,"河北"); tm.put(s2,"河南"); tm.put(s3,"湖南"); tm.put(s4,"湖北"); System.out.println(tm); } }
范例三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 /* 统计字符串"aababcabcdabcde"每个字符出现的次数,并按以下格式输出: a(5)b(4)c(3)d(2)e(1) */ import java.util.TreeMap; public class Test { public static void main(String[] args) { String str="aababcabcdabcde"; TreeMap<Character,Integer> tm=new TreeMap<>(); for(int i=0;i<str.length();i++){ Character c=str.charAt(i); if(tm.containsKey(c)){ int count=tm.get(c); count++; tm.put(c,count); }else { tm.put(c,1); } } StringBuilder sb=new StringBuilder(); tm.forEach((character, integer)->sb.append(character).append("(").append(integer).append(")")); System.out.println(sb); } } /* a(5)b(4)c(3)d(2)e(1) */
可变参数 可变参数 本质上是一个数组。
作用 :在形参中接收多个数据。
格式 :
数据类型…参数名称
注意事项 :
形参列表中可变参数只有一个
可变参数必须放在形参列表的最后面
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Test { public static void main(String[] args) { System.out.println(sum(1, 2, 3, 4)); System.out.println(sum(1, 2, 3, 4, 5)); System.out.println("大于7的数字有"+compare(7, 11, 9, 4, 6, 7)+"个"); } public static int sum(int...args){ int s=0; for (int num:args){ s+=num; } return s; } public static int compare(int num,int...args){ int c=0; for(int i=0;i<args.length;i++){ if(args[i]>num){ c++; } } return c; } } /* 10 15 大于7的数字有2个 */
Collections java.util.Collections 是集合工具类。
作用 :Collections不是集合,而是集合的工具类 。
常用方法 addAll 格式 :
public static boolean addAll(Collection c,T…elements)
说明 :
批量添加元素
shuffle 格式 :
public static void shuffle(List<?> list)
说明 :
打乱List集合元素的顺序
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.ArrayList; import java.util.Collections; public class Test { public static void main(String[] args) { ArrayList<String> list=new ArrayList<>(); System.out.println("addAll方法"); Collections.addAll(list, "saf","fasf","df","fsa"); System.out.println(list+"\n"); System.out.println("shuffle方法"); Collections.shuffle(list); System.out.println(list); } } /* addAll方法 [saf, fasf, df, fsa] shuffle方法 [saf, df, fsa, fasf] */
范例 例1 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /* 班级里有一些学生。 要求:被点到的学生不会再被点到。 若班级中所有学生点完,则开始下一轮点名 */ import java.util.ArrayList; import java.util.Collections; import java.util.Random; import java.util.Scanner; public class Test { public static void main(String[] args) { ArrayList<String> list=new ArrayList<>(); Collections.addAll(list,"张三","李四","王五","甲","乙","丙","丁"); ArrayList<String> list1=new ArrayList<>(); int i=1; Scanner s=new Scanner(System.in); Random r=new Random(); while(true){ System.out.print("若想退出输入0,非零继续循环,请输入:"); String in=s.nextLine(); if(in.compareTo("0")==0){ break; } System.out.println("第"+i+"轮点名"); list1.addAll(list); while(list1.size()!=0){ int num=r.nextInt(list1.size()); System.out.println(list1.get(num)); list1.remove(num); } list1.clear(); i++; } } }
例2 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 /* 定义一个map集合,键用表示省份名称province,值表示市city,但是市有多个,添加完毕后,遍历结果格式如下: 江苏省=南京市,扬州市,苏州市 湖北省=武汉市,孝感市,宜昌市 河北省=唐山市,邢台市,廊坊市 */ import java.util.*; public class Test { public static void main(String[] args) { ArrayList<String> city1=new ArrayList<>(); city1.add("南京市"); city1.add("扬州市"); city1.add("苏州市"); ArrayList<String> city2=new ArrayList<>(); city2.add("武汉市"); city2.add("孝感市"); city2.add("宜昌市"); ArrayList<String> city3=new ArrayList<>(); city3.add("唐山市"); city3.add("邢台市"); city3.add("廊坊市"); HashMap<String,ArrayList<String>> m=new HashMap<>(); m.put("江苏省",city1); m.put("湖北省",city2); m.put("河北省",city3); Set<Map.Entry<String,ArrayList<String>>> entries=m.entrySet(); for(Map.Entry<String,ArrayList<String>> entry:entries){ StringJoiner sj=new StringJoiner(","); ArrayList<String> value = entry.getValue(); for(String s:value){ sj.add(s); } System.out.println(entry.getKey()+"="+sj); } } } /* 江苏省=南京市,扬州市,苏州市 湖北省=武汉市,孝感市,宜昌市 河北省=唐山市,邢台市,廊坊市 */
不可变集合 不可变集合 :集合不可变,不能添加,修改,删除。
格式 在List,Set,Map接口中,均存在静态of方法,可获得一个不可变的集合。
方法 :static List of(E…element)
方法 :static Set of(E…element)
创建一个具有指定元素的set集合对象,元素不能重复
方法 :static <K,V> Map<K,V> of(E…element)
创建一个具有指定元素的Map集合对象,元素不能重复,键值对数量最多10个,超过10个用ofEntries方法
Properties properties是一个双列集合,拥有Map集合的所有特点,本身也有一些特有方法,可把集合的数据按照键值对的形式写到配置文件(后缀.properties)当中,也可把配置文件中的数据,读取到集合中。
propertise集合没有泛型,可以添加任意类型数据,但一般添加字符串类型数据。
常用方法 store 格式1 :
public void store(Writer writer,String comments)
格式2 :
public void store(OutputStream out,String comments)
说明 :把集合中的数据以键值对的形式写入到文件中,第二个参数为写入的注释
load 格式1 :
public void load(Reader reader)
格式2 :
public void load(InputStream inStream)
说明 :读取本地文件里的数据。
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.io.*; import java.util.Properties; public class Test { public static void main(String[] args) throws IOException { Properties p1=new Properties(); p1.put("user","zhangsan"); p1.put("password","123456"); BufferedWriter bw=new BufferedWriter(new FileWriter("a.properties")); p1.store(bw,"用户和密码"); bw.close(); Properties p2=new Properties(); BufferedReader br=new BufferedReader(new FileReader("a.properties")); p2.load(br); System.out.println(p2); } } /*a.properties内容 #用户和密码 #Tue Apr 04 17:00:17 CST 2023 password=123456 user=zhangsan */ /*打印内容 {password=123456, user=zhangsan} */