java学习笔记 集合续

本文详细介绍了Java中Map接口及其实现类,包括HashMap、LinkedHashMap、Hashtable和TreeMap的特点和使用方法。探讨了Map的基本操作和遍历技巧,并对比了各实现类的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

四,Map

0.概述
Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组用于保存Map里的key,另外一组用于保存Map里的value,key和value都可以使任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。
key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的、确定的value。如果把Map里所有key放在一起,它们就组成了一个Set集合(所有的key没有顺序,key与key之间不能重复),实际上Map确实包含了一个keySet()方法,用于返回Map里所有key组成的Set集合;如果把Map里所有value放在一起,它们又非常类似于一个List,元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中的索引不是整数值,而是以另一个对象作为索引。
Map有时候也被称为字典,或者关联数组。

HashMap:底层用哈希表数据结构,线程不同步,可以存入null键,null值
LinkedHashMap:基于哈希表又融入了链表,可以Map集合进行增删提高效率
Hashtable:底层是哈希表数据结构,线程同步,不可以存入null键,null值,效率较低
TreeMap:底层是二叉树数据结构,可以对Map中的键进行排序

1.Map接口中的常用方法
(1)void clear(): 删除该Map对象中的所有key-value对
(2)boolean containsKey(Object key): 查询Map中是否包含指定的key,如果包含返回true
(3)boolean containsValue(Object value): 查询Map中是否包含一个或多个value,如果包含返回true
(4)Set entrySet(): 返回Map中包含的key-value对所组成的Set集合
(5)Object get(Object key): 返回指定可以所对应的value;如果该Map中不包含该key,返回null
(6)boolean isEmpty(): 查询Map是否为空,如果为空返回true
(7)Set keySet(): 返回该Map中所有key组成的set集合
(8)Object put(Object key, Object value):添加一个key-value对,如果当前Map中已有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对
(9)void putAll(Map m): 将指定Map中的key-value对复制到本Map中
(10)Object remove(Object key):删除指定key对应的key-value对,返回被删除key所关联的value,如果该key不存在,返回null
(11)boolean remove(Object key, Object value):java8新增的方法,删除指定key、value所对应的key-value对,如果从Map中成功删除,返回true
(12)int size(): 返回该Map里的key-value对的个数
(13)Collection values():返回该Map里所有value组成的Collection

2.遍历Map的方式
(1)使用keySet,将Map转成Set集合,通过Set的迭代器取出Set集合中的每一个元素,就是Map集合中的所有的键,再通过get方法获取键对应的值

public class MapTest {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "aaaa");
        map.put(2, "bbbb");
        map.put(3, "cccc");
        System.out.println(map);

        //
        // 获取方法:
        // 第一种方式: 使用keySet
        // 需要分别获取key和value,没有面向对象的思想
        // Set<K> keySet() 返回所有的key对象的Set集合

        Set<Integer> ks = map.keySet();
        Iterator<Integer> it = ks.iterator();
        while (it.hasNext()) {
            Integer key = it.next();
            String value = map.get(key);
            System.out.println("key=" + key + " value=" + value);
        }
    }

}
/*
{1=aaaa, 2=bbbb, 3=cccc}
key=1 value=aaaa
key=2 value=bbbb
key=3 value=cccc
*/

(2)通过values获取所有值,不能获取key对象

public class MapTest {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "aaaa");
        map.put(2, "bbbb");
        map.put(3, "cccc");
        System.out.println(map);

        // 第二种方式:
        // 通过values 获取所有值,不能获取到key对象
        // Collection<V> values()

        Collection<String> vs = map.values();
        Iterator<String> it = vs.iterator();
        while (it.hasNext()) {
            String value = it.next();
            System.out.println(" value=" + value);
        }
    }

}
/*
{1=aaaa, 2=bbbb, 3=cccc}
 value=aaaa
 value=bbbb
 value=cccc
*/

(3)Map.Entry,面向对象向的思想,将Map集合中的键和值映射关系打包为一个对象,将该对象存入Set集合,所具备的方法有:
Object getKey(): 返回该Entry里包含的key值
Object getValue(): 返回该Entry里包含的value值
Object setValue(V value): 设置该Entry里包含的value值,并返回新设置的value值

public class MapTest {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "aaaa");
        map.put(2, "bbbb");
        map.put(3, "cccc");
        System.out.println(map);

        // 第三种方式: Map.Entry对象 推荐使用 重点
        // Set<Map.Entry<K,V>> entrySet()           
        // 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象
        Set<Map.Entry<Integer, String>> es = map.entrySet();
        Iterator<Map.Entry<Integer, String>> it = es.iterator();
        while (it.hasNext()) {

            // 返回的是封装了key和value对象的Map.Entry对象
            Map.Entry<Integer, String> en = it.next();

            // 获取Map.Entry对象中封装的key和value对象
            Integer key = en.getKey();
            String value = en.getValue();

            System.out.println("key=" + key + " value=" + value);
        }

    }

}
/*
{1=aaaa, 2=bbbb, 3=cccc}
key=1 value=aaaa
key=2 value=bbbb
key=3 value=cccc
*/

3.HashMap和Hashtable
HashMap和Hashtable都是Map接口的典型实现类,它们之间的关系完全类似于ArrayList和Vector的关系。Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable性能高一点,但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好。Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会引发NullPointerException异常,但HashMap可以使用null作为key或value。

class Person {
    private String name;
    private int age;

    Person() {

    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {

        return this.name.hashCode() + age * 37;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Person) {
            Person p = (Person) obj;
            return this.name.equals(p.name) && this.age == p.age;
        } else {
            return false;
        }
    }

    @Override
    public String toString() {

        return "Person@name:" + this.name + " age:" + this.age;
    }
}

public class HashMapTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        HashMap<Person, String> hm = new HashMap<Person, String>();
        hm.put(new Person("jack", 20), "1001");
        hm.put(new Person("rose", 18), "1002");
        hm.put(new Person("lucy", 19), "1003");
        hm.put(new Person("hmm", 17), "1004");
        hm.put(new Person("ll", 25), "1005");
        System.out.println(hm);
        System.out.println(hm.put(new Person("rose", 18), "1006"));

        Set<Entry<Person, String>> entrySet = hm.entrySet();
        Iterator<Entry<Person, String>> it = entrySet.iterator();
        while (it.hasNext()) {
            Entry<Person, String> next = it.next();
            Person key = next.getKey();
            String value = next.getValue();
            System.out.println(key + " = " + value);
        }


    }

}
/*
{Person@name:jack age:20=1001, Person@name:rose age:18=1002, Person@name:lucy age:19=1003, Person@name:hmm age:17=1004, Person@name:ll age:25=1005}
1002
Person@name:jack age:20 = 1001
Person@name:rose age:18 = 1006
Person@name:lucy age:19 = 1003
Person@name:hmm age:17 = 1004
Person@name:ll age:25 = 1005
*/

4.LinkedHashMap
LinkedHashMap使用双向链表维护key-value对的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。LinkedHashMap可以避免对HashMap、Hashtable里的key-value对进行排序,同时又可避免使用TreeMap所增加的成本。LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap,但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时将有较好的性能。

public class LinkedHashMapTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        LinkedHashMap scores = new LinkedHashMap();
        scores.put("语文" , 80);
        scores.put("英文" , 82);
        scores.put("数学" , 76);
        // 调用forEach方法遍历scores里的所有key-value对
        scores.forEach((key, value) -> System.out.println(key + "-->" + value));

    }
}
/*
语文-->80
英文-->82
数学-->76
*/

5.TreeMap
TreeMap就是一个红黑树结构,每个key-value对即为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有状态。
Set和Map的关系十分密切,java源码就是先实现了HashMap、TreeMap等集合,然后通过包装一个所有的value都为null的Map集合实现了Set集合类。

五,Collections,操作集合的工具类

java提供了一个操作Set、List和Map等集合的工具类–Collections,该工具类提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了将集合对象设置为不可变、对集合对象实现同步控制等方法。
以下对常用方法作一简单介绍,具体可见帮助文档。

1.排序操作
(1)void reverse(List list):反转指定List集合中元素的顺序
(2)void shuffle(List list):对List集合元素进行随机排序(shuffle方法模拟了“洗牌”动作)
(3)void sort(List list):根据元素的自然顺序对指定List集合元素按升序进行排序
(4)void sort(List list, Comparator c):根据指定Comparator产生的顺序对List集合元素进行排序
(5)void swap(List list,int i,int j):将指定List集合中的i元素和j元素进行交换
(6)void rotate(List list,int distance):当distance为正数,将list集合的后distance个元素整体移到前面;当distance为负数,将list集合的前distance个元素整体移到后面,该方法不会改变集合的长度

public class SortTest {
    public static void main(String[] args)
    {
        ArrayList nums = new ArrayList();
        nums.add(2);
        nums.add(-5);
        nums.add(3);
        nums.add(0);
        System.out.println(nums); // 输出:[2, -5, 3, 0]
        Collections.reverse(nums); // 将List集合元素的次序反转
        System.out.println(nums); // 输出:[0, 3, -5, 2]
        Collections.sort(nums); // 将List集合元素的按自然顺序排序
        System.out.println(nums); // 输出:[-5, 0, 2, 3]
        Collections.shuffle(nums); // 将List集合元素的按随机顺序排序
        System.out.println(nums); // 每次输出的次序不固定
    }

}

2.查找、替换
(1)int binarySearch(List list, Object key):使用二分搜索法搜索指定的List集合,以获得指定对象在List集合中的索引,如果要使该方法可以正常工作,必须保证List中元素已经处于有序状态
(2)Object max(Collection coll):根据元素的自然排序,返回给定集合中的最大元素
(3)Object max(Collection coll, Comparator comp):根据Comparator指定的顺序,返回给定集合中的最大元素
(4)Object mix(Collection coll):根据元素的自然排序,返回给定集合中的最小元素
(5)Object mix(Collection coll, Comparator comp):根据Comparator指定的顺序,返回给定集合中的最小元素
(6)void fill(List list, Object obj):使用指定元素obj替换指定List集合中的所有元素
(7)int frequency(Collection c, Object o):返回指定集合中指定元素的出现次数
(8)int indexOfSubList(List source, List target):返回子List对象在父List对象中最后一次出现的位置索引,如果父List中没有出现这样的子List,返回-1
(9)boolean replaceAll(List list, Object oldVal, Object newVal):使用一个新值newVal替换List对象的所有旧址值

public class SearchTest
{
    public static void main(String[] args)
    {
        ArrayList nums = new ArrayList();
        nums.add(2);
        nums.add(-5);
        nums.add(3);
        nums.add(0);
        System.out.println(nums); // 输出:[2, -5, 3, 0]
        System.out.println(Collections.max(nums)); // 输出最大元素,将输出3
        System.out.println(Collections.min(nums)); // 输出最小元素,将输出-5
        Collections.replaceAll(nums , 0 , 1); // 将nums中的0使用1来代替
        System.out.println(nums); // 输出:[2, -5, 3, 1]
        // 判断-5在List集合中出现的次数,返回1
        System.out.println(Collections.frequency(nums , -5));
        Collections.sort(nums); // 对nums集合排序
        System.out.println(nums); // 输出:[-5, 1, 2, 3]
        //只有排序后的List集合才可用二分法查询,输出3
        System.out.println(Collections.binarySearch(nums , 3));
    }
}

3.同步控制
Collections提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
从前面学习中可以看出,java中常用的集合框架中的实现类HashSet、TreeSet、ArrayList、LinkedList、HashMap、TreeMap都是线程不安全的。Collections提供了多个类方法可以把它们包装成线程同步的集合。

public class SynchronizedTest
{
    public static void main(String[] args)
    {
        // 下面程序创建了四个线程安全的集合对象
        Collection c = Collections
            .synchronizedCollection(new ArrayList());
        List list = Collections.synchronizedList(new ArrayList());
        Set s = Collections.synchronizedSet(new HashSet());
        Map m = Collections.synchronizedMap(new HashMap());
    }
}

4.设置不可变集合
(1)emptyXxx():返回一个空的,不可变的集合对象,此处的集合既可以是List,也可以是sortedSet、Set,还可以是Map、SortMap等
(2)singletonXxx():返回一个只包含指定对象(只有一个或一项元素)的、不可变的集合对象,此处的集合既可以是List,还可以是Map
(3)unmodifiableXxx():返回指定集合对象的不可变视图,此处的集合既可以是List,也可以是Set、SortedSet,还可以是Map、SortedMap
上述三类方法的参数是原有的集合对象,返回值是该集合的只读版本,通过Collections提供的三类方法,可以生成只读的Collection和Map。
Eg:

public class UnmodifiableTest
{
    public static void main(String[] args)
    {
        // 创建一个空的、不可改变的List对象
        List unmodifiableList = Collections.emptyList();
        // 创建一个只有一个元素,且不可改变的Set对象
        Set unmodifiableSet = Collections.singleton("疯狂Java讲义");
        // 创建一个普通Map对象
        Map scores = new HashMap();
        scores.put("语文" , 80);
        scores.put("Java" , 82);
        // 返回普通Map对象对应的不可变版本
        Map unmodifiableMap = Collections.unmodifiableMap(scores);
        // 下面任意一行代码都将引发UnsupportedOperationException异常
        unmodifiableList.add("测试元素");   //①
        unmodifiableSet.add("测试元素");    //②
        unmodifiableMap.put("语文" , 90);   //③
    }
}

上述代码分别定义了一个空的、不可变的List对象,一个只包含一个元素的、不可变的Set对象和一个不可变的Map对象,不可变的集合智能访问集合元素,不可修改集合元素,所以上述①②③都将引发UnsupportedOperationException异常

回忆:Arrays,用于对数组操作的工具类

二分查找,数组需要有序 binarySearch(int[])
binarySearch(double[])

数组排序 sort(int[])
sort(char[])……
将数组变成字符串 toString(int[])
复制数组 copyOf()
复制部分数组 copyOfRange()
比较两个数组是否相同 equals(int[],int[])
将数组变成集合 List asList(T[])

练习:定义一个Person数组,将Person数组中的重复对象剔除

package test;
import java.util.*;
/*
 思路: 
1. 描述一个Person类 
2. 将数组转换为Arrays.asList() List 
3. Set addAll( list ) 
4. hashCode()且equals()
 */
class Person {
    public String name;
    public int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {

        return getClass().getName() + " : name=" + this.name + " age="
                + this.age;

    }

    // 重写hashCode和equals()
    public int hashCode() {

        return this.age;
    }

    public boolean equals(Object o) {
        Person p = null;
        if (o instanceof Person)
            p = (Person) o;
        return this.name.equals(p.name) && (this.age == p.age);
    }
}

public class Test {
    public static void main(String [] args){
        Person[] ps = new Person[] { new Person("jack", 34),
                new Person("lucy", 20), new Person("lili", 10),
                new Person("jack", 34) };
        // 遍历数组
        System.out.println(Arrays.toString(ps));
        // 2. 将自定义对象数组转换为List集合
        List<Person> list = Arrays.asList(ps);
        // 3. 将List转换为Set
        Set<Person> set = new HashSet<Person>();
        set.addAll(list);
        System.out.println(set);

    }

}
/*
[test.Person : name=jack age=34, test.Person : name=lucy age=20, test.Person : name=lili age=10, test.Person : name=jack age=34]
[test.Person : name=jack age=34, test.Person : name=lucy age=20, test.Person : name=lili age=10]
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值