-----------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代替。
问题:为什么要将集合变数组?
答:为了限定对集合的操作,即不能进行增删操作。