一、概述
Map
是一种存储键值对的存储容器,而且保证键的唯一性。提供一种以“键”标识“值”的数据存储方式。接口形式为:Map<K,V>
,其中K是此映射所维护的键的类型,V是映射值的类型。其有两个常用子类,HashMap
和TreeMap
,另有HashTable
与HashMap
功能类似,是早期版本。三者特点与不同如下:
HashMap
:JDK1.2版本出现,底层使用哈希表数据结构,允许使用null
作为键值和null
值,线程非同步。TreeMap
:JDK1.2版本出现,底层使用二叉树数据结构,可用于按照键值排序,线程非同步。Hashtable
:JDK1.0版本出现,底层使用哈希表数据结构,不允许使用null
作为键值,也不允许null
作为值,线程同步。
HashMap
和Hashtable
的键值需要实现hashCode
和equals
功能,应为键值是唯一的,如同在HashSet
中添加元素一样,因为底层为哈希表结构。
Collections
是集合框架中的工具类,其内部有全是一些静态的方法,可以直接使用,如sort(List<T> list)
对列表进行排序,max(...)
求最大值,min(...)
求最小值,binarySearch(...)
二分查找等。
二、Map中的共性方法
clear()
清空Map。containsKey(Object o)
判断是否包含key。containsValue(Object o)
判断是否包含value。put(K, V)
添加键-值对。putAll(Map<? extends K, ? extends V>)
添加另一个集合的内容。get(Object key)
获取key对应的值。size()
获取大小。values()
获取所有的值。
keySet
和entrySet
的应用
Map
没有想List
和Set
的那种迭代器遍历方式,Map的遍历一般可以使用keySet
和entrySet
两种方式。
使用keySet
遍历Map
,这种方式是先获取键的集合对象,然会遍历键的集合,最后根据键的获取对应的值。示例代码如下:
import java.util.*;
class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("hello", "hi");
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
// 取出键的集合
Set<String> set = map.keySet();
Iterator<String> it = set.iterator();
// 遍历键的集合
while(it.hasNext()) {
String key = it.next();
// 根据键获取值
System.out.println("(" + key + "," + map.get(key) +")");
}
}
}
// 执行结果为
(key1,value1)
(key2,value2)
(key3,value3)
(hello,hi)
另一中Map
的遍历方式是通过entrySet
实现的。示例代码如下:
import java.util.*;
class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("hello", "hi");
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
// 取出键-值的集合
Set<Map.Entry<String, String>> set = map.entrySet();
// 遍历键-值集合
Iterator<Map.Entry<String, String>> it = set.iterator();
while(it.hasNext()) {
Map.Entry<String, String> entry = it.next();
// 通过getKey和getValue方法可获取键和值
System.out.println("(" + entry.getKey() + "," + entry.getValue() +")");
}
}
}
// 执行结果为
(key1,value1)
(key2,value2)
(key3,value3)
(hello,hi)
Map
使用自定义类作为键
每个学生对应一个地址,学生有姓名和年龄属性。姓名和年龄相同视为同一个学生。学生类Student
需要完成hashCode
和equals
的方法功能,当hashCode
的返回值相同且equals
返回为true
时,认为两个键是同一个键,内容的值被覆盖。可以看到结果无序,且Student(lisi1, 21)
的值唯一为tianjin
,示例代码如下:
import java.util.*;
class Student {
private String name;
private int age;
Student(String name,int age) {
this.name = name;
this.age = age;
}
/**
* 复写hashCode方法
*/
public int hashCode() {
return name.hashCode()+age*34;
}
/**
* 复写equals方法
*/
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 String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString() {
return name+":"+age;
}
}
class Main {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap<Student, String>();
map.put(new Student("lisi1",21),"beijing");
map.put(new Student("lisi1",21),"tianjin");
map.put(new Student("lisi2",22),"shanghai");
map.put(new Student("lisi3",23),"nanjing");
map.put(new Student("lisi4",24),"wuhan");
// 获取键-值集合
Set<Map.Entry<Student,String>> entrySet = map.entrySet();
// 获取键-值集合迭代器
Iterator<Map.Entry<Student,String>> it = entrySet.iterator();
// 遍历集合
while(it.hasNext()) {
Map.Entry<Student,String> entry = it.next();
// 打印学生和对应的地址
System.out.println(entry.getKey() + "/" + entry.getValue());
}
}
}
// 执行结果为
lisi4:24/wuhan
lisi2:22/shanghai
lisi1:21/tianjin
lisi3:23/nanjing
TreeMap
使用简介
与TreeSet
类似,TreeSet
是对值按一定顺序排序,而TreeMap
则是对键进行按一定顺序排序。同样的,作为TreeMap
键的类需要实现Comparable
接口或者在创建TreeMap
对象时指定比较器(实现Comparator
接口的类)。下面通过一个实例来说明,例子内容为统计一串字符串中各个字母出现的次数,并按照字母顺序输出,这里自定义一个类CharKey
作为键,让其实现Comparable接口,并覆写hashCode
(不过这里用不到)与equals
方法:
import java.util.*;
import java.lang.*;
class CharKey implements Comparable<CharKey> {
private String key;
public CharKey(char key) {
this.key = String.valueOf(key);
}
public int compareTo(CharKey o) {
return this.key.compareTo(o.getKey());
}
public String getKey() {
return this.key;
}
// 用第一个字符的Unicode代码点作为哈希值
public int hashCode() {
return this.key.codePointAt(0);
}
// 判断字符是否相同
public boolean equals(Object o) {
if(!(o instanceof CharKey)) {
throw new RuntimeException("类型不匹配");
}
CharKey key = (CharKey) o;
return this.key.equals(key.getKey());
}
}
class Main {
public static void main(String[] args) {
// 待统计字符串
String str = "sdfgzxcvasdfxcvdf";
// 创建TreeMap对象,CharKey作为键,Integer作为值
TreeMap<CharKey, Integer> map = new TreeMap<CharKey, Integer>();
// 扫描字符串,统计各个字母出现的次数
for(int i=0; i<str.length(); i++) {
// 构造第i个字母的键
CharKey key = new CharKey(str.charAt(i));
if(map.get(key) == null) { // 键不存在则是第一次,直接添加1
map.put(key, 1);
} else {
// 键存在,则将值取出再加1放入
map.put(key, map.get(key)+1);
}
}
// 遍历统计的结果
Set<Map.Entry<CharKey, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<CharKey, Integer>> it = entrySet.iterator();
while(it.hasNext()) {
Map.Entry<CharKey, Integer> entry = it.next();
CharKey key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key.getKey()+"("+ value +")");
}
}
}
// 执行结果为
a(1)
c(2)
d(3)
f(3)
g(1)
s(2)
v(2)
x(2)
z(1)
三、Collections
使用简介
Collections
为java.util
包中为集合框架提供的工具类,其内部全部是静态函数,如sort
(对List
进行排序),完全的定义为:public static <T extends Comparable<? super T>> void sort(List<T> list)
也就是说T
需要时实现Comparable
接口,且T
的父类实现了Comparable
接口(即使用父类的排序方法),或者使用sort(List<T> list, Comparator<? super T> c)
来指定比较器来实现自定义的数据比较。
import java.util.*;
class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
list.add("z");
list.add("haje");
list.add("vssddda");
list.add("abcdef");
// 排序前的list
System.out.println(list);
Collections.sort(list);
// 排序后的list
System.out.println(list);
}
}
// 执行结果如下(排序前后的list)
[hello, z, haje, vssddda, abcdef]
[abcdef, haje, hello, vssddda, z]
binarySearch
的使用,使用binarySearch
的前提必须对list
进行排序,否则无法正常使用,其返回值为所查找的元素的位置,如果元素不存在,那么返回的为(-(插入点) - 1)
,如这个值为index
,那么~index
即为这个元素正在该插入的位置。同样binarySearch
也可以指定自定义比较器。示例代码如下:
import java.util.*;
class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
list.add("z");
list.add("haje");
list.add("vssddda");
list.add("abcdef");
Collections.sort(list);
int index = Collections.binarySearch(list, "b");
/* 返回值为元素的位置,如果不存在则返回负数index,
然而~index则表示元素应该插入的位置 */
System.out.println("Index:" + ~index);
}
}
使用Collections.reverseOreder()
获取,默认逆序比较器。或者Collections.reverseOrder(比较器)
将指定比较器转化为逆序比较器。synchronizeCollection()
,synchronizedList()
,...
使集合具有多线程访问能力,shuffle(List)
将集合随机打乱等等。
四、Arrays
使用简介
Arrays
为数组操作的一个工具类,功能有asList()
将数组转换为集合,但是不能对此集合进行增删操作,否则会报UnsupportedOperationException
异常。
对基本数据类型和非基本数据类型的不同写法:
// 基本数据类型数组转集合
int[] nums = {1, 3, 2};
List<int[]> list = Arrays.asList(nums);
// 基本数据类型数组转集合
int[] strs = {"hello", "hi", "oo"};
List<String> list = Arrays.asList(strs);
集合转成数组,使用toArray(t[] t)
方法可以将集合转为数组,这里的参数数组的大小如果小于list.size()
那么方法会重新创建一个长度为list.size()
大小的数组,然后返回,如果大于list.size()
,那么会使用参数的数组,并将其返回,示例代码如下:
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
list.add("hi");
String[] strs = list.toArray(new String[0]);
五、JDK1.5新特性
1.泛型:再上一篇集合框架已经介绍过了,即提高了程序的安全性。
2.增强for
循环:格式为for(数据类型 变量:被遍历Collection或数组)
。与传统for
循环的区别在于,增强型for
必须有被遍历的对象,且无法访问下标。示例代码如下:
ArrayList<String> list = new ArrayList<String>();
for(String s : list) {
// 操作
}
int[] arr = {2, 3, 4};
for(int i : arr) {
// 操作
}
3.自动装箱/自动拆箱:在上一篇博客已经介绍过。
4.可变参数:当方法的参数类型相同且不确定有多少个时,可以使用可变参数。注意:当还有其他参数时,可变参数必须放在参数列表的最后面,如show(String, int...)
,示例代码如下:
show(1, 2, 3);
show();
show(1, 2, 3, 4, 5, 6);
public void show(int... arr) {
// arr就是一个数组
}
5.静态导入:如Arrays
类内部全部是静态方法,可以将其直接导入使用,不需要再指明方法名,但是需要注意的是当有方法重名时,需要指明所属类,当类重名时,需要指明所属包。示例代码如下:
import java.util.*;
import static java.util.Arrays.*;
class Main {
public static void main(String[] args) {
int[] arr = {2, 4, 2, 1, 9, 8};
// 直接调用Arrays内的静态方法sort
sort(arr);
System.out.println(Arrays.toString(arr));
}
}
六、其他对象
System
类,不能被实例化,内部全是静态方法,常用的有一些内部对象,如in
和out
用于标准输入输出。一些系统相关方法,如currentTimeMillis()
用于获取系统时间(1970年1月1日到现在的毫秒数),getProperties()
获取虚拟机环境信息等。Rumtime
类,通过getRuntime()
获取对象,可以通过Rumtime
对象执行系统命令,如getRuntime().exec("cmd");
,通过destory()
杀掉进程等。Date
类,可以通过SimpleDateFormat
将Date
类转变成自定义格式。Calendar
类,通过getInstance()
获取实例,然后可以通过get(Calendar.YEAR)
获取年份或者其他等。Math
类,一些常用方法ceil(double )
上取整,floor(double )
下取整,random()
返回[0, 1)
之间的浮点数值。Random
类,可以很方便地产生伪随机数。