关闭

黑马程序员——Java集合框架(三)之Map集合、Collections与Arrays工具类

234人阅读 评论(0) 收藏 举报
分类:

-----------android培训java培训、java学习型技术博客、期待与您交流!------------

                  Map集合

一、概述

 Map集合存储的元素是键值对,即将键和值一对一对往里存,而且要保证键的唯一性。

 问题思考:

  1.如何保证键的唯一性?

   答:当存储时,发现该键已经在集合中存在,则会覆盖集合中的已存在的键的对应值,同时会返回被覆盖的值。

  2. 什么时候使用Map集合?

    答:当数据之间存在映射关系时,应考虑使用Map集合。


二、常见共性方法

 1.添加

  V put(K key,V value):将指定的值与此映射中的指定键关联。

  V putAll(Map<? extends K, ? extends V> m):从指定映射中将所有映射关系复制到此映射中。

 2.删除

  void clear():从此映射中移除所有映射关系。

  V remove(Object key):如果存在一个键的映射关系,则将其从此映射中移除。

 3.判断

  boolean containKey(Object key):如果此映射包含指定键的映射关系,则返回 true。

  boolean containValue(Object value):如果此映射将一个或多个键映射到指定值,则返回 true。

  boolean isEmpty():如果此映射未包含键-值映射关系,则返回true。

 4.获取

  V get(Object key):返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。

  int size():返回此映射中的键-值映射关系数。

  Collection<V>values():返回此映射中包含的值的 Collection 视图。

  Set<K> keySet():返回此映射中包含的键的 Set 视图。

  Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射关系的 Set 视图。


三、常见子类对象特点

 Map接口有三个常见实现的子类,分别为TreeSet、HashSet和Hashtable。

  Hashtable:底层是哈希表数据结构,不可以存入null键和null值,该线程是同步的。Hashtable是jdk1.0版本出现的,运行效率较低。

  HashMap:底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。HashMap是jdk1.2版本出现的,运行效率较高。

  TreeMap:底层是二叉树数据结构,线程不同步。可以用于给集合中的键进行排序。

 注:发现Map集合和Set集合很像,其实Set集合的底层就是使用了Map集合。

 问题思考:为什么TreeMap集合排序的是键而不是值呢?

  答:因为TreeMap集合中的键具有唯一性,而值不唯一,因此只能给键进行排序。集合中的键的要实现自然排序就必须要实现Comparable接口,让键具有比较性。如果键不具备比较性,也通过想集合的构造函数传入指定的比较器来进行排序。


四、两种取出方式

 1.keySet()方法:

   实现方式:创建Map集合,并调用keySet()方法,将Map集合中的所有键都存入Set集合中。再通过Set集合获取迭代器对象,通过迭代器取出所有Set集合中的键值,获取键值后,可通过Map集合的get方法获取所有对应的值。

   代码示例:

import java.util.*;
/*
需求:用keySet方取出Map集合中的所有键值对。

思路:
	1.创建一个Map集合,并给Map集合添加键值对。
	2.调用keySet方法,将所有的键存入到Set集合中。
	3.再将Set集合中的键用迭代器的方式取出,并且每取出一个键都将用Map集合中的getValue方法获取
	其值,同时打印出键值对。
*/
class  KeySetDemo
{
	public static void main(String[] args) 
	{
		Map<String,String> m=new HashMap<String,String>();

		m.put("01","lisi01");
		m.put("02","lisi02");
		m.put("03","lisi03");

		Set<String> s=m.keySet();
		for (Iterator<String> it=s.iterator();it.hasNext() ; )
		{
			String key=it.next();
			String value=m.get(key);
			System.out.println(key+"::"+value);
		}
	}
}
  程序运行后的结果如下图:


 2.entrySet()方法:

  实现方式:创建Map集合,并调用entrySet()方法将Map集合的映射关系存入到Set集合中,而这个映射关系就是Map.Entry。再通过Set集合获取迭代器对象,通过迭代方式取出所有的键值对的映射关系,获得映射关系后可以调用其内部的getkey()和getValue()分别获得键和值。

  Map.Entry其实是一个接口,该接口是Map接口的一个内部接口。其内部实现原理如下:

interface Map<K,V>
{
	public static interface Entry<K,V>
	{
		K getKey();
		V getValue();
	}
}

class HashMap<K,V> implements Map<K,V>
{
	class Node<K,V> implemets Map.Entey<K,V>
	{
		public final K getKey(){return key;}
		public final V getValue(){return value;}
	}
}

  代码示例:

import java.util.*;
/*
需求:用entrySet方法取出Map集合的所有键值对。

思路:
	1.创建一个Map集合,并添加键值对。
	2.调用entrySet方法将键值对的映射关系存入Set集合中。
	3.获取迭代器对象取出Set集合的所有映射关系对象,并在取出的同时,用该对象获取相应
	的键和值,并打印出来。
*/

class EntrySetDemo 
{
	public static void main(String[] args) 
	{
		Map<String,String> m=new HashMap<String,String>();

		m.put("02","lisi02");
		m.put("03","lisi03");
		m.put("04","lisi04");

		Set<Map.Entry<String,String>> s=m.entrySet();

		for (Iterator<Map.Entry<String,String>> it=s.iterator();it.hasNext() ; )
		{
			Map.Entry<String,String> me=it.next();
			String key=me.getKey();
			String value=me.getValue();
			System.out.println(key+"::"+value);	
		}
	}
}
  程序运行后的结果如下图:



五、Map拓展——多层集合嵌套

 当集合中的元素仍然是一个集合时,就称为集合嵌套。

 代码示例:Map集合嵌套List集合。

import java.util.*;

/*
需求:多层集合嵌套示例。
*/

class MapDemo 
{
	public static void main(String[] args) 
	{
		HashMap<String,List<Student>> school=new HashMap<String,List<Student>>();

		List<Student> room1=new ArrayList<Student>();
		List<Student> room2=new ArrayList<Student>();

		school.put("jiuyeban",room1);
		school.put("jichuban",room2);

		room1.add(new Student("zhangsan",10));//注意:如果数字前面带0,默认表示八进制数。
		room1.add(new Student("lisi",8));

		room2.add(new Student("wangwu",4));
		room2.add(new Student("zhouliu",5));

		for (Iterator<String> iter=school.keySet().iterator();iter.hasNext() ; )
		{
			String roomName=iter.next();
			List<Student> room=school.get(roomName);
			System.out.println(roomName);
			getStudentInfo(room);
		}

	}

	public static void getStudentInfo(List<Student> room)
	{
		for (Iterator<Student> it=room.iterator();it.hasNext() ; )
		{
			Student stu=it.next();
			System.out.println(stu);
		}
	}
}
class Student implements Comparable<Student>
{
	private String name;
	private int id;

	Student(String name,int id)
	{
		this.name=name;
		this.id=id;
	}

	public String getName()
	{
		return name;
	}

	public int getId()
	{
		return id;
	}
	
	//打印Student对象时,直接打印自定义返回的内容
	public String toString()
	{
		return this.name+"--"+this.id;
	}

	public int hashCode()
	{
		return name.hashCode()+id*23;
	}

	public boolean equals(Object obj)
	{
		if (!(obj instanceof Student))
		{
			throw new ClassCastException("类型不相同");
		}

		Student s=(Student)obj;
		return this.name.equals(s.name)&&this.id==s.id;
	}

	//实现Comparable接口时带了泛型参数Student,因此复写时应参数类型为Student才能覆盖。
	public int compareTo(Student s)
	{
		int num=new Integer(this.id).compareTo(new Integer(s.id));
		if (num==0)
		{
			return this.name.compareTo(s.name);
		}
		return num;
	}
}
 程序运行后的结果如下图:


 Ps:运用多层集合嵌套,应学会举一反三去应用解决问题。


六、代码练习

 练习1:

  给HashMap集合中的元素去重复,即保证键的唯一性。例如;学生对象的属性有姓名和年龄,如果姓名和年龄都相同,就认为是同一个学生。学生还有地址,把学生对象和地址作为键值对存入集合中,地址用String类型来描述。

import java.util.*;
/*
练习1:给HashMap集合中的元素去重复,即保证键的唯一性。
例如;学生对象的属性有姓名和年龄,如果姓名和年龄都相同,就认为是同一个学生。学生还有地址,
把学生对象和地址作为键值对存入集合中,地址用String类型来描述。

思路:
	1.给HashMap集合元素去重复,那么需要给存入对象自定义HashCode方法,使姓名和年龄都相同的学生对象,返回
	的哈希值一致。
	2.复写equals方法,如果姓名和年龄都相同则返回ture,即不存入集合中。
*/
class  MapTest1
{
	public static void main(String[] args) 
	{
		HashMap<Student,String> sm=new HashMap<Student,String>();

		sm.put(new Student("lisi01",20),"chengdu");
		sm.put(new Student("lisi03",23),"wuhan");
		sm.put(new Student("lisi01",20),"changsha");

		Set<Student> s=sm.keySet();
		for (Iterator<Student> it=s.iterator();it.hasNext() ; )
		{
			Student key=it.next();
			String value=sm.get(key);
			System.out.println(key+":::"+value);
		}

	}
}

class Student implements Comparable<Student>
{
	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;
	}
	
	//打印Student对象时,直接打印自定义返回的内容
	public String toString()
	{
		return this.name+"--"+this.age;
	}

	public int hashCode()
	{
		return name.hashCode()+age*23;
	}

	public boolean equals(Object obj)
	{
		if (!(obj instanceof Student))
		{
			throw new ClassCastException("类型不相同");
		}

		Student s=(Student)obj;
		return this.name.equals(s.getName())&&this.age==s.getAge();
	}

	//实现Comparable接口时带了泛型参数Student,因此复写时应参数类型为Student才能覆盖。
	public int compareTo(Student s)
	{
		int num=new Integer(this.age).compareTo(new Integer(s.getAge()));
		if (num==0)
		{
			return this.name.compareTo(s.getName());
		}
		return num;
	}
}<strong>
</strong>
  程序运行后的结果如下图:


 练习2:

  在练习1的基础上对学生对象的年龄进行排序。

import java.util.*;
/*
练习2:在练习1的基础上对学生对象的年龄进行排序。


思路:
	1.通过实现Comparator接口自定义一个比较器。
	2.比较器类中复写compare方法,使其按照学生年龄排序。
*/
class  MapTest2
{
	public static void main(String[] args) 
	{
		TreeMap<Student,String> sm=new TreeMap<Student,String>();

		sm.put(new Student("lisi01",20),"chengdu");
		sm.put(new Student("lisi07",23),"wuhan");
		sm.put(new Student("lisi02",19),"changsha");
		sm.put(new Student("lisi05",23),"beijing");

		Set<Student> s=sm.keySet();
		for (Iterator<Student> it=s.iterator();it.hasNext() ; )
		{
			Student key=it.next();
			String value=sm.get(key);
			System.out.println(key+":::"+value);
		}

	}
}

class MyComparetor implements Comparator<Student>
{
	public int compare(Student s1,Student s2)
	{
		int num=new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
		if (num==0)
		{
			return s1.getName().compareTo(s2.getName());
		}
		return num;
	}
}
class Student implements Comparable<Student>
{
	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;
	}
	
	//打印Student对象时,直接打印自定义返回的内容
	public String toString()
	{
		return this.name+"--"+this.age;
	}

	public int hashCode()
	{
		return name.hashCode()+age*23;
	}

	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;
	}

	//实现Comparable接口时带了泛型参数Student,因此复写时应参数类型为Student才能覆盖。
	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;
	}
}
  程序运行后的结果如下图:


 练习3:

  “383ejdfoajfoejlanfdiflehgoafjoe”获取该字符串中字母出现的次数。希望打印结果的形式为:a(1)c(3)...

import java.util.*;

/*
练习3:“383ejdfoajfoejlanfdiflehgoafjoe”获取该字符串中字母出现的次数。

希望打印结果的形式为:a(1)c(3)...

分析:通过结果发现,每一个字母都有对应的次数,说明字母和次数之间
都有映射关系。当数据之间存在映射关系时,可以先考虑Map集合。
*/
class MapTest3 
{
	public static void main(String[] args) 
	{
		String s="838ejdfoajfoejlanfdiflehgoafjoe";
		System.out.println(stringCount(s));
	}

	public static String stringCount(String str)
	{
		char [] ch=str.toCharArray();

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

		//字符数组中的字符存入map集合,并用值的形式记录个数。
		for (int i=0;i<ch.length ;i++ )
		{
			if (!(ch[i]>='a'&ch[i]<='z'||ch[i]>='A'&ch[i]<='Z'))
			{
				continue;
			}
			Integer value=map.get(ch[i]);
			if (value==null)
			{
				map.put(ch[i],1);
			}
			else
			{
				value++;
				map.put(ch[i],value);
			}
		}
		StringBuilder sb=new StringBuilder();

		for (Iterator<Character> it=map.keySet().iterator();it.hasNext() ; )
		{
			char key=it.next();
			int value=map.get(key);

			//将map集合的键值对以指定的形式存入StringBuilder容器中
			sb.append(key+"("+value+")");
		}

		return sb.toString();

	}
}
  程序运行后的结果如下图:



Collections与Arrays工具类

一、Collections工具类

 Collections是专门用给集合进行操作的工具类,常见的功能有:

 1.给List集合进行排序:

   static <T extends Comparable<? super T>> void sort(List<T> list):  根据元素的自然顺序 对指定列表按升序进行排序.

   static <T> void sort(List<T> list,Comparator< ? super T> c) : 根据指定比较器产生的顺序对指定列表进行排序。

  2.返回给定Collection的最大元素:

  static <T extends Object & Comparable<? super T>> T max(Collection< ? extends T> coll):根据元素的自然顺序,返回给定 collection 的最大元素。

  static <T> T max(Collection<? extends T> coll, Comparator<?super T> comp):根据指定比较器产生的顺序,返回给定 collection 的最大元素。

  3.使用二分搜索法搜索List列表,以获得指定对象:

  static <T> int binarySearch(List<? extends Comparable<? superT>> list, T key):让List集合的元素具备比较性,按照元素的自然排序进行二分查找。

  static<T> int binarySearch(List<? extends T> list, T key,Comparator<? super T> c):按照比较器指定的顺序进行二分查找。

  4.对List集合元素进行替换反转操作:

  static <T> void fill(List<? super T> list, T obj):将List集合中所有元素替换成指定元素。 

  static <T>boolean replaceAll(List<T> list, T oldVal, T newVal):使用另一个值替换列表中出现的所有某一指定值。

  static void reverse(List<?> list): 反转指定列表中元素的顺序。

  5.强行逆转指定比较器的顺序:  

  static <T>Comparator<T> reverseOrder(Comparator<T> cmp):返回一个比较器,它强行逆转指定比较器的顺序。

  6.反转Collection集合的自然顺序:

  static <T>Comparator<T> reverseOrder():返回一个比较器,它强行逆转实现了Comparable 接口的对象 collection 的自然顺序。

  7.将线程不安全的集合转换成线程安全的集合,可以转换的集合包括Collection、Set、Map、List等。

  内部机制:定义一个新的类,并实现List集合,在实现给集合时,给所有的方法都进行同步,即使用同一把锁。


二、Arrays工具类

 Arrays工具类是专门用于操作数组的,常见的功能有:(具体方法参见API文档)

 1.对各种类型的数组进行二分查找。

 2.比较两个同类型的数组的内容是否相同。

 3.对各种类型的数组进行排序。

 4.返回各基本类型数组的字符串表现形式。

 5.将数组变成List集合,变成集合后,该集合不能进行增删操作,因为数组的长度是固定的,如果进行了增删操作,则会发生UnSupportedOperationException异常。

  注:将Collection集合变数组,可以用Collection接口的toArray方法。如果想转换成某个类型数组,只需传入某个类型的数组即可,假如传入的数组的长度大于容器的元素的个数,则其他位置全部用null代替。

  问题:为什么要将集合变数组?

   答:为了限定对集合的操作,即不能进行增删操作。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4806次
    • 积分:202
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档