集合框架(Map和Collections)

一、概述

Map是一种存储键值对的存储容器,而且保证键的唯一性。提供一种以“键”标识“值”的数据存储方式。接口形式为:Map<K,V>,其中K是此映射所维护的键的类型,V是映射值的类型。其有两个常用子类,HashMapTreeMap,另有HashTableHashMap功能类似,是早期版本。三者特点与不同如下:

  • HashMap:JDK1.2版本出现,底层使用哈希表数据结构,允许使用null作为键值和null值,线程非同步。
  • TreeMap:JDK1.2版本出现,底层使用二叉树数据结构,可用于按照键值排序,线程非同步。
  • Hashtable:JDK1.0版本出现,底层使用哈希表数据结构,不允许使用null作为键值,也不允许null作为值,线程同步。

HashMapHashtable的键值需要实现hashCodeequals功能,应为键值是唯一的,如同在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()获取所有的值。

keySetentrySet的应用

Map没有想ListSet的那种迭代器遍历方式,Map的遍历一般可以使用keySetentrySet两种方式。
使用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需要完成hashCodeequals的方法功能,当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使用简介

Collectionsjava.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类,不能被实例化,内部全是静态方法,常用的有一些内部对象,如inout用于标准输入输出。一些系统相关方法,如currentTimeMillis()用于获取系统时间(1970年1月1日到现在的毫秒数),getProperties()获取虚拟机环境信息等。
  • Rumtime类,通过getRuntime()获取对象,可以通过Rumtime对象执行系统命令,如getRuntime().exec("cmd");,通过destory()杀掉进程等。
  • Date类,可以通过SimpleDateFormatDate类转变成自定义格式。
  • Calendar类,通过getInstance()获取实例,然后可以通过get(Calendar.YEAR)获取年份或者其他等。
  • Math类,一些常用方法ceil(double )上取整,floor(double )下取整,random()返回[0, 1)之间的浮点数值。
  • Random类,可以很方便地产生伪随机数。

你可能感兴趣的:(java,程序员,集合,map,Collection)