Map集合因其独特的特性而被广泛使用,使用Map集合注意到的问题也自然有很多,Map集合能够更加有序,高效的操作数据。今天,便要一同去遨游在Map集合中的海洋,感受Map集合的神奇。
*Map集合
一、Map集合的特点:
该集合存储键值对,是一对一对的往里面存,而且要保证元素的唯一性。
二、Map集合的基本方法:
1,增加
put ( key , value )
putAll ( key , value )
2,删除
clear ( )
remove ( key )
3,判断
containsKey ( key )
containsValue ( value )
isEmpty ( )
4,获取
get ( key )
size ( )
values ( )
entrySet ( )
keySet ( )
Map
| - - Hashtable :底层是哈希表数据结构,不可以存入null作为键和值的情况,该集合是同步的。 JDK1.0,效率低
| - - HashMap :底层是哈希表数据结构,允许使用null键和null值,该集合是不同步的。JDK1.2,效率高
| - - TreeMap :底层是二叉树数据结构,可以用于给map集合中的键进行排序,线程不同步。
经上述分析,Set集合和Map集合较为相似,其实Set集合底层就是使用了Map集合。
三、Map集合的取出方式
1,Set
import java.util.*;
class MapDemo2
{
public static void main(String[] args)
{
Map map = new HashMap();
//添加元素,添加元素如果出现相同的键,那么后添加的值会覆盖原有的键对应的值。并put方法会返回被覆盖的值
map.put("01","zhangsan1"); //put方法有返回值,返回的是相同键的原来的值
map.put("02","zhangsan2");
map.put("03","zhangsan3");
map.put("04","zhangsan4");
//先获取map集合中的所有键的Set集合,keySet方法
Set keySet = map.keySet();
//通过迭代器迭代Set集合
Iterator it = keySet.iterator();
while(it.hasNext()){
String key = it.next();
//通过key获取对应的value
String value = map.get(key);
System.out.println(key+":"+value);
}
}
}
2,Set < Map.Entry< k , v>> entrySet:将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry。其实Entry也是一个接口,它是Map接口中的一个内部接口。理解代码如下:
import java.util.*;
class MapDemo2
{
public static void main(String[] args)
{
Map map = new HashMap();
//添加元素,添加元素如果出现相同的键,那么后添加的值会覆盖原有的键对应的值。并put方法会返回被覆盖的值
map.put("01","zhangsan1"); //put方法有返回值,返回的是相同键的原来的值
map.put("02","zhangsan2");
map.put("03","zhangsan3");
map.put("04","zhangsan4");
//将Map集合中的映射关系取出,存入到Set集合中
Set> entrySet = map.entrySet();
Iterator> it = entry.iterator();
while(it.hasNext()){
Map.Entry me = it.next();
String key = me.getKey();
String value = me.getValue();
System.out.println(key+":"+value);
}
}
}
四、Map集合的项目应用:
1,需求:对学生对象的年龄进行升序排序
因为数据时以键值对形式存在的,所以要使用可以排序的Map集合,TreeMap。代码如下:
import java.util.*;
class Student implements Comparable //定义Student类,因为用到排序,所以实现了Comparable接口
{
private String name;
private int age;
Student(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String toString(){
return this.name+":"+age;
}
public int hashCode(){ //复写hashCode和equals方法,如果集合用的是HashMap,则需要覆盖此方法
return name.hashCode()+age*34;
}
public boolean equals(Object obj){
if(!(obj instanceof Student)){
throw new ClassCastException("类型不匹配");
}
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.age;
}
public int compareTo(Student s){ //定义元素默认的依据年龄排序的方法
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num==0){
return this.name.compareTo(s.name);
}
return num;
}
}
class StuNameComparator implements Comparator //定义姓名比较器
{
public int compare(Student s1,Student s2){
int num = s1.getName().compareTo(s2.getName());
if(num == 0){
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
}
return num;
}
}
class MapTest2
{
public static void main(String[] args)
{
TreeMap tm = new TreeMap(new StuNameComparator());//将姓名比较器传入TreeMap集合中
tm.put(new Student("lisi1",21),"beijing");
tm.put(new Student("lisi1",21),"tianjin");
tm.put(new Student("lisi2",22),"shanghai");
tm.put(new Student("lisi3",23),"nanjing");
tm.put(new Student("lisi4",24),"wuhan");
Iterator> it = tm.entrySet().iterator(); //通过获取键值对映射关系的方式迭代Map集合
while(it.hasNext()){
Map.Entry me = it.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+">>"+addr);
}
}
}
2,需求:“sdfgzxvasdfxcvdf"获取该字符串中字母出现的次数,希望打印结果为a ( 1 ) c ( 2 ) .....
通过结果发现,每一个字母都有对应的次数,说明字母和次数之间都有映射关系,可以选择map集合,因为Map集合中存放的就是映射关系。
思路:1,将字符串转换成字符数组,因为要对每一个字母进行操作;2,定义一个Map集合,因为打印字母有顺序,所以使用TreeMap集合;3,遍历字符数组,将每一个字母作为键去查Map集合,如果返回null,将该字母和1存入到Map集合中,如果返回不是null,说明该字母在Map集合内已经存在并有对应的次数,那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到Map集合中,覆盖掉原来键所对应的值;4,将Map集合中的数据变成指定的字符串形式返回。代码如下:
import java.util.*;
class MapTest3
{
public static void main(String[] args)
{
String s = charCount("sdfgzxvasdfxcvdf");
System.out.println(s);
}
public static String charCount(String str){
//1,将字符串转换为字符数组
char[] chs = str.toCharArray();
//2,定义一个TreeMap集合
TreeMap tm = new TreeMap();
int count = 0;
//3,遍历数组
for (int x=0; x= 'a' && chs[x]<='z' || chs[x]>='A'&&chs[x]<='Z'))
continue;
Integer value = tm.get(chs[x]);
if(value != null){
count = value;
}
count++;
tm.put(chs[x],count);
count = 0;
}
//定义StringBuilder缓冲区
StringBuilder sb = new StringBuilder();
//迭代Map,将Map的键和值存入字符串缓冲区
Iterator> it = tm.entrySet().iterator();
while(it.hasNext()){
Map.Entry me = it.next();
Character key = me.getKey();
Integer value = me.getValue();
sb.append(key+"("+value+")");
}
return sb.toString();
}
}
Map集合被使用时因为具备映射关系,所以会出现映射的嵌套关系,如传智播客具备就业办和基础班,基础班和就业班各拥有自己的学生姓名和学号,也就是毕老师所讲的大圈套小圈思想。此时,使用迭代器迭代传智播客,再在迭代的过程中迭代出就业办和基础班的学生信息即可解决Map多映射关系集合。
六、Collections与Collection的区别
Collection为util包中的接口,而Collections为util中的工具类,具备的都是静态方法,常见的有集合的排序,取最大值,反转等常用方法。
Collections逆转比较器方法如下:
import java.util.*;
class CollectionsDemo2
{
public static void main(String[] args)
{
orderDemo();
}
public static void orderDemo(){
TreeSet ts = new TreeSet(Collections.reverseOrder(new StrComparator()));//此处传入Collections工具类中的强行逆转比较器,使得代码更为简单
ts.add("abcde");
ts.add("aaa");
ts.add("kkk");
ts.add("ccc");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
class StrComparator implements Comparator//以前的做法,定义比较器来进行反转
{
public int compare(String s1,String s2){
return s2.compareTo(s1);
}
}
七、Arrays工具类
Arrays:用于操作数组的工具类,里面都是静态方法。
asList ( arr ):将数组变为集合。此方法中如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素,如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
将数组变为List集合有什么好处?
可以使用集合的思想和方法来操作数组中的元素。注意,将数组变成集合,不可以使用集合的增删方法,因为数组的长度是固定的,如果增删,会发生异常。
数组变为集合的代码如下:
import java.util.*;
class ArraysDemo
{
public static void main(String[] args)
{
String[] arr = {"abc","cc","kkkk"};
List list = Arrays.asList(arr); //asList方法是将数组转换为集合
System.out.println("contains:"+list.contains("cc")); //转换为集合后可使用集合的方法
// list.add("qq"); //此处将抛出异常,因为数组长度是不可变的
System.out.println(list);
int[] nums1 = {2,4,5};
Integer[] nums2 = {2,4,5};//此处同时涉及自动装箱
List list1 = Arrays.asList(nums1);
List list2 = Arrays.asList(nums2);
System.out.println(list1); //此处打印结果为数组的哈希码
System.out.println(list2); //此处打印结果为[2,4,5]
}
}
1.指定类型的数组要定义多长呢?
当指定类型的数组小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size,当指定类型的数组长度大于了集合的size,就不会新创建数组,而是使用了传递进来的数组。所以创建一个刚刚好的数组最好。
2,为什么要将集合转换为数组?
为了限定对元素的操作,不需要进行增删了
import java.util.*;
class CollectionToArray
{
public static void main(String[] args)
{
ArryaList al = new ArrayList();
al.add("abc1");
al.add("abc2");
al.add("abc3");
String[] arr = al.toArray(new String[al.size()]);//通过Collection接口中的toArray方法将集合转换为数组
System.out.println(Arrays.toString(arr)); //直接用Arrays工具类的toString方法进行数组的打印
}
}
八、高级for循环(for each循环)
格式:
for ( 数据类型 变量名 : 被遍历的集合或者数组 ) {
}
对集合进行遍历,只能获取元素,但是不能对集合进行过多操作。
迭代器除了遍历还可以进行remove集合中元素的动作。
如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
传统for循环和高级for的区别:
高级for有一个局限性,必须有被遍历的目标。建议在遍历数组的时候还是希望使用传统for,因为传统for可以定义索引。
理解代码如下:
import java.util.*;
class ForEachDemo
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add("abc1");
al.add("abc2");
al.add("abc3");
//1.5之后可以简化书写为下:
for(String str : al){
System.out.println(str);
}
/*
未用foreach代码如下:
Iterator it = al.iterator();
while(it.hasNext){
System.out.println(it.next());
}
*/
HashMap hm = new HashMap();
hm.put(1,"a");
hm.put(2,"b");
hm.put(3,"c");
//第一种用高级for遍历map集合的方法
Set keySet = hm.keySet();
for(Integer key:keySet){
System.out.println(key+":"+hm.get(key));
}
//第二种用高级for遍历map集合的方法
Set> entrySet = hm.entrySet();
for(Map.Entry me : entrySet){
System.out.println(me.getKey()+"::"+me.getValue());
}
}
}
可变参数其实就是数组的简写形式,不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可。隐式的将这些参数封装成了数组。在使用时注意可变参数一定要定义在参数列表的最后面。
class ParamMethodDemo
{
public static void main(String[] args)
{
show("abc",2,4,5,6,7,8);
}
//可变参数的做法
public static void show(String str,int... arr){
System.out.println(arr.length);
}
/*
//以前的做法
public static void show(int[] arr){
}
*/
}
当类名重名时,需要指定具体的包名,当方法重名时,指定具备所有的对象或类。
静态导入示例:
// import java.util.*;
import static java.util.Arrays.*; //导入的是Arrays这个类中所有静态成员
import static java.lang.System.*; //导入的是System类中所有的静态成员
class StaticImport
{
public static void main(String[] args)
{
int[] arr = {3,1,5};
//Arrays.sort(arr);
sort(arr); //静态导入之后可以省略类名
//int index = Arrays.binarySearch(arr,1);
int index = binarySearch(arr,1);
//System.out.println(Arrays.toString(arr));
out.println(toString(arr));
out.println("index="+index);
}
}