黑马程序员_day20_Map集合

集合框架中的另一个顶层接口,Map

Map:用于存储具备着对应关系的键和值。

而且要保证键的唯一性。

一次存储一对儿元素。

Collection一次存储一个元素,称之为单列集合。

Map集合一次存储一对儿元素,称之为双列集合。

 

一、Map常见方法:

1,添加。

v put(k,v):

void putAll(map);

2,删除。

void clear():清空集合。

v remove(key):通过键删除元素,并返回对应的值。

3,判断。

boolean containsKey(key);

boolean containsValue(value);

boolean isEmpty();

4,获取。

value get(key):根据键获取值。如果键不存在,返回null。可以用来判断一个集合中是否存在某一个键。

5,获取map中所有的元素(对应关系)

Map是不具备迭代器的,获取原理就是将map集合先转成set集合。然后再使用迭代器。

演示:

现在准备存储一些有对应关系的元素,比如学生的姓名和学号。而且学号是唯一的。学号和姓名之间存在对应的关系。

使用到了map集合。 

key的类型是:Integer。    value的类型是:String

Map<Integer,String> map = new HashMap<Integer,String>();

public static void mapMethodDemo4(Map<Integer,String> map){

map.put(4, "lisi");

map.put(1, "qianyi");

map.put(3, "zhangsan");

map.put(7, "zhouqi");

put方法的返回值。在map集合中,存储了相同的键,新值会替换旧值。put方法会返回旧值。如果没有相同的键,返回的是null

System.out.println(map.put(3,"haha"));该语句的结果为zhangsan

 

二、 最重点的部分:如何获取map中所有的元素

方式一:    

 //通过keySet方法获取map集合中所有的键集

Set<Integer> keySet = map.keySet();

//通过Set集合的迭代器获取所有的键。

Iterator<Integer> it = keySet.iterator();

while(it.hasNext()){

Integer key = it.next();

//通过map集合的get方法可以获取键对应的值。 

String value = map.get(key);

System.out.println(key+"::::"+value);

}

}

 

方式二、

通过entrySet()方法来获取map集合中所有的键和值的对应关系

 Map.Entry : 其实entry就是Map接口中的一个内部接口而已。

其形式如下:

public interface Map{

  public static interface Entry{// 内部接口。 

}

}

获取所有键和值的方式二代码实现:

//通过map集合的entrySet,将键值映射关系存储到Set集合中。 

Set<Map.Entry<Integer, String>> entrySet = map.entrySet();

//通过set集合获取迭代器。 

Iterator<Map.Entry<Integer, String>> it = entrySet.iterator();

while(it.hasNext()){

Map.Entry<Integer, String> me = it.next();

Integer key = me.getKey();

String value = me.getValue();

通过Map.Entry接口(Map中的内部类被public static修饰) 中的getKey(),getValue()方法来分别获取键和值。

System.out.println(key+"----"+value);

}

}

 

values方法。获取map集合中所有的值

通过values()获取map中所有的值。返回的是Collection,因为值是有可能是重复的。 而键是唯一的。

Collection<String> coll = map.values();

Iterator<String> it = coll.iterator();

while(it.hasNext()){

System.out.println(it.next());

}

}

 

三、Map体系

|--Hashtable:底层是哈希表数据结构,是线程同步的,不允许null作为键,null作为值。

|--HashMap:底层是哈希表数据结构,是线程不同步的,允许null作为键,null作为值。替代了Hashtable

|--TreeMap:可以对Map集合中的键进行指定顺序的排序,默认是使用键的自然顺序。当然也可以使用比较器。

 

四、HashMap练习

需求:

将学生对象和归属地的映射关系存储到HashMap集合中,并全部取出。

学生对象中包含着姓名和年龄这样的属性。

归属地:这里仅用字符串表示即可。

 思路:

1,对学生对象进行描述。同姓名同年龄视为同一人。

HashMap<Student,String> hm = new HashMap<Student, String>();

hm.put(new Student("xiaoming1",28), "北京");

hm.put(new Student("xiaoming7",23), "天津");

// hm.put(new Student("xiaoming7",23), "西藏");同名同龄

hm.put(new Student("xiaoming2",25), "河北");

hm.put(new Student("xiaoming0",24), "河南");

 

//通过entrySet()取出元素。

Set<Map.Entry<Student,String>> entrySet = hm.entrySet();

Iterator<Map.Entry<Student,String>> it = entrySet.iterator();

while(it.hasNext()){

Map.Entry<Student, String> me = it.next();

Student stu = me.getKey();

String addr = me.getValue();

 

System.out.println(stu.getName()+":"+stu.getAge()+":"+addr);

}

}

同名同龄视为一人,需对Student类进行hashCodeequals方法覆盖

 

定义StudenthashCode的方法。

public int hashCode(){

final int NUMBER = 37;

return name.hashCode()+age*NUMBER;

}

 

定义Studentequals方法。

public boolean equals(Object obj){

if(this == obj)如果传入的对象和当前对象是同一对象

return true;

if(!(obj instanceof Student))是否是Student类或其子类

throw new ClassCastException("类型不匹配,无法比较");

Student stu = (Student)obj;向下转型

return this.name.equals(stu.name) && this.age == stu.age;

}

 

若对上述Student的年龄姓名进行排序,用TreeMap

TreeMap<Student,String> tm = new TreeMap<Student,String>(new Comparator<Student>(){

public int compare(Student s1,Student s2){

int temp = s1.getName().compareTo(s2.getName());

若是要倒序排序 就只把temp语句中的s1s2 调换下位

return temp==0?s1.getAge()-s2.getAge():temp;

}

});这里直接把比较器给写出来了,用匿名类

 

keyset()取出。

Iterator<Student> it = tm.keySet().iterator();

Set<Student> keySet = tm.keySet();

Iterator<Student> it = keySet.iterator();

while(it.hasNext()){

Student key = it.next();

String addr = tm.get(key);

System.out.println(key.getName()+"::::"+key.getAge()+":::"+addr);

}

}

}

 

五、LinkedHashMap和增强for循环

HashMap<Integer, String> hm = new LinkedHashMap<Integer, String>();

hm.put(5, "wangwu");

hm.put(1, "zhaoliu");

hm.put(9, "zhouqi");

hm.put(4, "lisi");

Iterator<Map.Entry<Integer,String>> it = hm.entrySet().iterator();

hm.entrySet()是一个单列的对应关系集合的实例。

while(it.hasNext()){

Map.Entry<Integer,String> me = it.next();

Integer key = me.getKey();

String value = me.getValue();

System.out.println(key+":"+value);

}

LinkedHashMap是怎么存怎么取,是有序的。

可用foreach来替代迭代器。两种方式

1、对只取出键的情况的替代

//Set<Integer> keySet = hm.keySet();

for(Integer i : hm.keySet()){这里可直接写hm.keySet()可省略上句的Set<Integer> keySet = hm.keySet();for(Integer i : keySet)

String value = hm.get(i);

System.out.println(i+":"+value);

}

2、对取对应关系情况的迭代器替代

for(Map.Entry<Integer,String> me : hm.entrySet()){

Integer key = me.getKey();

String value = me.getValue();

System.out.println(key+"::::"+value);

}

 

六、Map集合练习  查表

练习:

"abcdvbzvadsza"获取该字符串中每个字母出现的次数,

要求结果是: 字母(次数)字母(次数如:a(2)b(3)c(1)e(5)...

思路:

1,发现字母和次数之间存在对应关系也就是映射关系,用map集合。

2map集合中应该存储是字母和次数,键是字母,值是次数。 

3,将map作为一张表,使用了查表法思想。对字符串中的每一个字母都去map表中进行次数的查找。

如果没有找到对应的次数,就将该字母和1存储到表中。

如果找到了对应的次数,就将该次数取出,并对该次数自增后,在将该字母和新的次数重新存入表中,对应map集合而言,键相同,值会覆盖。

4,所有的字母都查询完毕后,map表就有了所有字母和次数的对应关系,

 5,在将这些对应关系变成指定格式的字符串即可。

代码实现:

String str = "abc+advba-zvad,asza";

String s = getCharCount(str);

System.out.println(s);

public static String getCharCount(String str) {

//1,先将字符串转成字符数组。 

char[] chs = str.toCharArray();

//2,定义一个map容器。作为存储字母和次数的表。 

Map<Character,Integer> map = new TreeMap<Character,Integer>();

for (int i = 0; i < chs.length; i++) {

 

 

//判断是否是字母。

if(!(chs[i]>='a' && chs[i]<='z' || chs[i]>='A' &&  chs[i]<='Z'))

continue;

//3,将遍历到的每一个字母都作为键去查表。获取次数。

Integer value = map.get(chs[i]);

int count = 0;

if(value!=null){

count = value;

}

count++;

map.put(chs[i], count);

划线部分是对下面代码的优化。

/*if(value==null)//该字母不存在。将该字母和1次存入。

map.put(chs[i], 1);

else{

value++;

map.put(chs[i], value);

}*/

}

// System.out.println(map);

 

return mapToString(map);

}

创建mapToString功能:

private static String mapToString(Map<Character, Integer> map) {

StringBuilder sb = new StringBuilder();

 

for(Map.Entry<Character, Integer> me : map.entrySet()){

Character key = me.getKey();

Integer value = me.getValue();

sb.append(key+"("+value+")");

}

return sb.toString();

}

 

七、CollectionMap的区别?

什么时候使用map呢?

当分析问题时,发现其中存在着映射关系,这时应该想到两个容器,一个是map,一个是数组。

如果映射关系的一方是有序的编号(可以作为索引),而且映射关系的个数确定,建议使用数组。

否则,就使用map集合。 

凡是映射关系的出现,都可以使用查表法的思想去解决问题。 

 

其他集合什么时候使用呢?

如果要保证元素的唯一性,不存在映射关系,必须先考虑使用Set集合。

如果不需要保证唯一性,直接考虑List集合。

 

八、集合工具类Collections

Collections:专门用于操作的集合的工具类。 

该类中的方法都是静态的。

//演示对List集合进行元素的指定方式的排序。

List<String> list = new ArrayList<String>();

list.add("abcde");

list.add("nba");

list.add("haha");

System.out.println(list);

1、  //按照自然顺序。排序。

     //Collections.sort(list);

//排序,按照长度排序。

Collections.sort(list,new ComparatorByLength());

这里需创建ComparatorByLength比较器

System.out.println(list);

2//获取该集合的最值。

// String max = Collections.max(list);按自然顺序(字典顺序)

String max = Collections.max(list,new ComparatorByLength());

按自定义的自然顺序(长度)当然需要自定义该比较器

System.out.println(max);

其原理是:

public static String getMax(List<String> list){

//1,定义个变量用于记录最大值,初始化为集合中的任意一个值。

Iterator<String> it = list.iterator();

String max  = it.next();

while(it.hasNext()){

String temp = it.next();

if(temp.compareTo(max)>0)

max = temp;

}

return max;

}

 

3、 对已有排序逆转。反向。逆向比较器Collections.reverseOrder()

TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());

将自然顺序强转成逆向顺序。

TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new ComparatorByLength()));

对已有的比较器new ComparatorByLength()进行逆向。对已有排序逆转。

4、 重点 目前我们学习的集合框架中的大部分常用的集合对象都是线程不同步的。

万一为多线程访问,会出现线程安全问题。

可以通过Collections中的同步集合方法,将非同步的集合,转换成同步的集合。 

synchronized+集合类型   比如:synchronizedList(List<t> list)

返回指定列表支持的(线程安全的)同步列表

synchronizedList(Map<k,v> m)返回指定映射支持的同步映射

 

九、 Arrays:操作数组的工具类。

里面都是静态方法。 

1、二分查找

binarySearch(float[]a,int fromIndex,int toIndex,float key)

使用二分搜索法来搜索指定数据类型数组的范围,以获得指定的值

binarySearch(int[]a,int key)

2、fill(int []a,int val)将指定的数值val分配给数组中的每个元素,就是对数组的每个元素用val初始化。

3、sort(int[]a,int fromIndex,int toIndex)对指定类型的数组指定的范围按数字升序进行排序

sort(int[]a)

4、toString(int[]a)返回指定数组内容的字符串表示形式

就不用再进行使用StringBuilder来进行数组向字符串转化的操作了

int[] arr = {45,12,89,24,87};

System.out.println(Arrays.toString(arr));

5、重要的一个方法:数组转成集合。 asList([]);

数组对象能有的就是一个属性length,使用了Arrays以后,也就具备那么几个方法。 

可是将数组转成集合后,就可以使用集合的方法来操作数组中的元素。 

 * 比如:contains方法。 indexOf

 * 这样做了可以有更多的方法对数组中的元素进行操作。

String[] arr = {"abc","haha","xixi"};

List<String> list = Arrays.asList(arr);

// System.out.println(list.contains("haha"));

System.out.println(list);

但是有些方法不能用,因为数组的长度是固定的。不能使用增或删的方法。 

// list.add("heihei");//UnsupportedOperationException

 

特殊情况:

int[] nums = {7,9,3};

 

List<int[]> list2 = Arrays.asList(nums);

System.out.println(list2.size());结果为1.

因为如果是基本数据类型的数组,在进行数组转集合时,会被视为一个基本类型的数组元素。

如果想将int[] nums = {7,9,3};的转换成3个元素为793的集合。可以写成Integer[] nums = {7,9,3};当然List<int[]>应改为List<Integer>

 

十、集合转成数组

集合转成数组。

使用的是Collection接口中的方法toArray方法。 

集合转成数组的原因,限制对元素的操作,比如增删。

List<String> list = new ArrayList<String>();

list.add("abc1");

list.add("abc5");

list.add("abc2");

//将集合转成了数组。 

String[] arr = list.toArray(new String[list.size()]);

System.out.println(Arrays.toString(arr));

}

注意:String[] arr = list.toArray(new String[list.size()]);这里的list.size()改为小于或等于list.size()的数字比如1,结果是abc1abc5abc2.

如果是大于的话,比如5,结果就会是abc1abc5abc2nullnull

十一、jdk1.5特性 可变参数

如果要传入多个参数,而且都是同一类型,可以定义数组类型的参数。

但是传递时,必须先有数组对象。

jdk1.5后,为了简化书写,出现了特性,可变参数。

同样还是代表数组,但是不需要在创建数组对象了,直接将数组中的元素作为参数传入即可。

它会自动的将这些参数封装到数组中。 

 

 局限性。可变参数必须定义在参数列表的最后面。

int sum = add5(4,1,7,4,8,9);

System.out.println(sum);

int sum1 = add5(5,3,2,4,5,7,7);

System.out.println("sum1="+sum1);

public static int add5(int... arr){

int sum = 0;

for(int x=0; x<arr.length; x++){

sum+=arr[x];

}

return sum;

}

而局限性就是:public static int add5(int... arr)中的参数列表里int...arr必须在列表的最后面。否则错误,如add5(int... arr,int a)

 

 

 

 

 

 

 

 

posted @ 2013-03-31 18:36  javawebsoa  Views(203)  Comments(0Edit  收藏  举报