集合(其二)
四.Set集合
2.TreeSet集合
(2)TreeSet集合实现对元素排序的方式二
采用二叉树数据结构,可对Set集合中的元素排序; 在开发中,我们很难按照我们所要的需求去修改已经写好的类,但是如果我们想不按照对象中具备的自然排序进行排序或者是想让不具备自然排序规则的对象能够按照我们的意愿进行排序的话,我们就应该为集合创建一个比较器,让集合自身具备比较功能。
实现步骤:先创建一个比较器实现Comparator接口并定义其compare()方法,再将实现比较器的子对象作为参数传入TreeSet的构造方法中(TreeSet set = new TreeSet(new 比较器))。
通过一个对字符串长度的排序范例,深入认识比较器的使用:(注释中包含注意点和知识点)
定义一个比较器:
<span style="font-family:KaiTi_GB2312;"><strong>package cn.itzixue.linkeedlist;
import java.util.Comparator;
public class ComparatorByLen implements Comparator {
//复写compare()方法
public int compare(Object o1, Object o2) {
//将传入的对象进行强转
String str1 = (String)o1 ;
String str2 = (String)o2 ;
//定义一个变量存放两个字符串长度的比较值(负数:小于;正数:大于;0:相等)
int temp = str1.length()-str2.length() ;
//如果两个字符串长度相同,则对其内容进行比较
return temp==0?str1.compareTo(str2):temp;
}
}
</strong></span>
将比较器传入TreeSet集合构造方法,对存入集合的内容进行排序并输出:
<span style="font-family:KaiTi_GB2312;"><strong> //将比较器对象作为TreeSet集合构造函数的参数传入
TreeSet ts = new TreeSet(new ComparatorByLen()) ;
//向集合中增加元素
ts.add("a") ;
ts.add("ab") ;
ts.add("bc") ;
ts.add("abc") ;
//使用Iterator将集合中的元素迭代输出
Iterator it = ts.iterator() ;
while(it.hasNext()){
System.out.println(it.next());
}</strong></span>
五.Map集合
Collection集合体系包括了List和Set两大分支,它是单列集合,一次添加一个元素。而Map集合体系是双列集合,每一次添加的是一对元素。Map集合中存储的是键值对,在存储时,必须保证键的唯一性。
1.Map集合的常用方法
初始化:
通过HashMap、TreeMap…来实例化Map对象:
例:Map<Integet,String> map = new HashMap<Integet,String>() ;
① 添加元素:
(返回值类型:value值的类型) put(key,value) ;
//往集合中添加一对键值对,此方法将返回前一个和key关联的值,如果没有则返回null。(存相同键值会覆盖)
(返回值类型:void) putAll(Map p) ; // 往集合中添加另一个集合的元素
② 删除元素:
(返回值类型:void) clear() ; //清空Map集合
(返回值类型:value值的类型) remoce(key) ;
//根据指定的key值删除这个键值对,并返回说删除键值对对应的值
③ 判断:
(返回值类型:boolean) containsKey(key) ; //判断集合中是否有包含指定键
(返回值类型:boolean) containsValue(value) ; //判断集合中是否包含指定值
(返回值类型:boolean) isEmpty() ; //判断集合是否为空
④ 获取集合中的元素:
(返回值类型:value值的类型) get(key) ;
//通过指定键获取值,如果没有该键,则返回null。可以通过返回值是否为null来判断是否包含该指定键
(返回值类型:int) size() ; //获取键值对的个数
2.取出Map集合中的元素
(1)方式一
原理:通过keySet()方法获取map中所有键所在的Set集合,再通过Set的迭代器获取到每一个键,在对每一个键获取其对应值(通过Map的get()方法)。
范例:
<span style="font-family:KaiTi_GB2312;"><strong> //创建一个map对象
Map<Integer,String> map = new HashMap<Integer,String>() ;
//向集合中添加元素
map.put(1,"a") ;
//创建一个Set容器用于存放map中的key值
Set<Integer> s = map.keySet() ;
//创建一个迭代器对象
Iterator it = s.iterator() ;
while(it.hasNext()){
//通过遍历Set集合取到键,并通过建取到值
String str = (String)map.get(it.next()) ;
System.out.println(str) ;
}</strong></span>
(2)方式二
通过将Map集合转换为Set集合,再将其进行迭代。使用Map集合对象的entrySet(),该方法将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.Entry类型。
范例:
<span style="font-family:KaiTi_GB2312;"><strong> //创建一个map对象
Map<Integer,String> map = new HashMap<Integer,String>() ;
//向集合中添加元素
map.put(1,"a") ;
//创建一个Set容器用于将Map集合作为元素传入
Set<Map.Entry<Integer, String>> entrySet = map.entrySet() ;
//创建一个迭代器对象
Iterator it = entrySet.iterator() ;
while(it.hasNext()){
//创建一个Map.Entry对象,用于存放通过遍历Set集合取到Map集合,便于操作其键和值
Map.Entry<Integer, String> me = it.next() ;
//通过Map.Entry对象获取其元素的键和值
Integer key = me.getKey() ;
String value = me.getValue() ;
System.out.println(key+" : "+value) ;
}</strong></span>
(3)取出Map集合中的所有值
范例:
Collection<String> values = map.values() ; // 返回值是一个Collextion集合
3.Map集合常用的子类
① Hashtable:内部结构是哈希表,使用时线程是同步的。(键值必须非空)
Hashtable集合有一个子类Properties集合,它是用来存储键值对性的配置文件信息,一般与IO技术相结合。
② HashMap:内部结构是哈希表,使用时线程是不同步的。(允许空作为键/值)
③ TreeMap:内部结构是二叉树,使用时线程是不同步的。(此类可以对Map集合中的键进行排序)
范例:
HashMap:(将学生对象和学生归属地通过键值存储于Map中)
定义一个学生类:
<span style="font-family:KaiTi_GB2312;"><strong>package cn.itzixue.example;
//创建一个学生类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
//覆写hashCode()方法
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
//覆写equals()方法
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}</strong></span>
通过Map集合存储元素:
<span style="font-family:KaiTi_GB2312;"><strong>public class HashMapDemo{
public static void main(String[] args){
//将学生对象和学生的归属地通过键与值存储到map集合中
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put( new Student("chen" ,38),"北京");
hm.put( new Student("zhao" ,24),"上海");
hm.put( new Student("qian" ,31),"广州");
hm.put( new Student("wang" ,28),"深圳");
//在存储元素时,如果键相同这将值覆盖为新值
hm.put( new Student("zhao" ,24),"汕头");
//定义一个迭代器
Iterator<Student> it = hm.keySet().iterator();
while(it.hasNext()){
Student key = it.next();
String value = hm.get(key);
System.out.println(key.getName() + ":" + key.getAge() + "---" + value);
}
}
}</strong></span>
TreeMap:(将以上Map集合进行排序)
创建一个比较器:
<span style="font-family:KaiTi_GB2312;"><strong>class ComparatorByName implements Comparator<Student> {
//覆盖原有compare()方法,通过名字排序
public int compare(Student s1, Student s2) {
int temp = s1.getName().compareTo(s2.getName());
}
}</strong></span>
将存入Map集合的元素进行排序:
<span style="font-family:KaiTi_GB2312;"><strong>public class HashMapDemo {
public static void main(String[] args) {
// 将学生对象和学生的归属地通过键与值存储到map集合中,并将比较器对象作为参数传入TreeMap构造方法中
TreeMap<Student, String> tm = new TreeMap<Student, String>(
new ComparatorByName());
tm.put(new Student("chen", 38), "北京");
tm.put(new Student("zhao", 24), "上海");
tm.put(new Student("qian", 31), "广州");
tm.put(new Student("wang", 28), "深圳");
//键相同值覆盖
tm.put(new Student("zhao", 24), "汕头");
//将Map集合转换为Set集合进行迭代
Iterator<Map.Entry<Student, String>> it = tm.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Student, String> me = it.next();
Student key = me.getKey();
String value = me.getValue();
System.out.println(key.getName() + ":" + key.getAge() + "---" + value);
}
}
}</strong></span>
六.集合框架使用技巧
集合体系的集合类有很多,为了更好地在开发中使用集合类,我们可以从以下几点来判断是用哪一个集合类:
(1)需要元素唯一(使用Set集合体系)
① 需要制定顺序:使用TreeSet
② 不需要指定顺序:使用HashSet
③ 需要一个存取顺序一致的集合(有序):使用LinkedHashSet
(2)不需要元素唯一(使用List集合体系)
① 需要频繁的增删操作:使用LinkedList
② 需要高效的查询速率:使用ArrayList
(3)需要存储键值对:使用Map集合体系
小技巧:
① 集合类的后缀名就是该集合所属的体系
② 集合类的前缀名就是集合的数据结构:
☆看到Array:就要想到数组数据结构,具有查询跨的特点,并且具有角标;
☆看到Link:就要想到链表数据结构,具有增删速率块的特点;
☆看到Hash:就要想到哈希表,元素唯一性的特点,并且元素需要覆盖hashCode()和equals()方法;
☆看到Tree:就要想到二叉树,可以用于排序,并且需要实现Comparable接口和Comparator接口。
③ 常用集合容器使用是线程都是不同步的。
七.Map集合的使用实例
存在映射关系的容器:数组、Map集合,其中如果关系一方有序就使用数组,如果没有有序编号就使用Map集合。使用Map集合时,如果发现可以保证唯一性的一方具备着顺序(如:a,b,c,…),则可以使用TreeMap.
通过以下一个获取该字符串中每一个字母出现的次数的范例,来深入认识Map集合的使用:(注意:注释中包含知识点和注意点)
<span style="font-family:KaiTi_GB2312;"><strong>package cn.itzixue.maptext;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/*
* 范例:获取字符串中每一个字母出现的次数
*
* 思路:
* ① 由分析可以发现,字母和次数之间存在着映射的关系,并且改关系次数很多;
* ② 由于存在不确定多对的映射关系,所以要想对其进行存储就可以使用数组和Map集合;
* ③ 由于关系一方没有有序编号,所以应该使用Map集合;
* ④ 由于可以保证唯一性的一方具备着顺序(字母顺序),所以可以使用TreeMap集合。
*/
public class MapTest {
/*
* 实现步骤:
* ① 由于所操作的对象是字符串中的字母,所以应该先将已有字符串变成字符数组,使用字符串对象的toCharArray()方法;
* ② 遍历所得到的字符数组,用每一个字母作为Map集合中的键去查询Map集合;
* ③ 如果该字母的键在Map集合中不存在,就应该将该字母作为键,并且将 1作为值将此映射关系存储到Map集合中;
* ④ 如果该字母的键在Map集合中存在,就应该将该字母键在Map集合中对应的值取出,并将其值+1;
* ⑤ 将该字母和修改后的值存储到Map集合中。由于键相同值将被覆盖成新值;
* ⑥ 将Map集合进行遍历操作,即可Map集合就记录所有字母的出现的次数。
*/
public static void main(String[] args) {
//定义一个字符串
String str = "sadJsdf/AgxFfDrSS-yeDx*xrqwA+ereet";
//调用自定义的getCharCount()方法得到所要的结果
String s = getCharCount(str);
System.out.println(s);
}
//为了体现面向对象思想,将此实现过程封装成getCharCount()方法。
public static String getCharCount(String str) {
//通过字符串对象的toCharArray()方法将传入的字符串变成字符数组
char[] chs = str.toCharArray();
//创建Map集合对象,由于可以保证唯一性的一方具备着顺序,所以使用TreeMap集合进行实例化
Map<Character, Integer> map = new TreeMap<Character, Integer>();
//对字符数组中的字符进行次数判断操作以及存储操作
for (int i=0;i<chs.length;i++) {
//判断字符组中的字符是否为26个字母(大小写),若有其他字符则略去
if (!(chs[i]>='a'&&chs[i]<='z'||chs[i] >='A'&& chs[i] <= 'Z'))
continue;
//将字符数组中的字符作为Map集合的键去查询Map集合。如果返回是null,则表明该字符是第一次出现
Integer value = map.get(chs[i]);
//定义一个计数器变量
int count = 1;
//判断返返回的值是否为null,如果不为空,则将在Map集合中取道的value+1并赋值给计数器变量count
//如果判断结果为空,则跳过这一步代码,直接将计数器变量count(值为1)的值与对应的字符存入Map集合中
if (value != null) {
count = value + 1;
}
//将字符与计数器变量存储到Map集合之中。根据键相同值覆盖原理不断更新Map集合
map.put(chs[i], count);
}
//调用输出方法
return mapToString(map);
}
//为了体现面向对象思想,将此实现过程封装成getCharCount()方法。
private static String mapToString(Map<Character, Integer> map) {
//定义一个StringBuilder临时容器
StringBuilder sb = new StringBuilder();
//使用Map集合的keySet()方法获取Map集合中键的集合,并进行迭代
Iterator<Character> it = map.keySet().iterator();
//迭代Map集合中键的集合
while (it.hasNext()) {
//分别获取Map集合元素的键和值
Character key = it.next();
Integer value = map.get(key);
//使用append()方法进行拼接
sb.append(key + "(" + value + ")");
}
//返回输出格式的结果
return sb.toString();
}
}
</strong></span>
Map集合的使用很广泛,在有映射关系时,可以优先考虑Map集合,该集合在查表法中的应用较为多见。