52、Java基础教程之集合体系·下

  • 1️⃣ 双列对象保存:Map
      • 1.1 HashMap 类与 Hashtable 类
    • 1.2 使用 lterator 获取 Map 集合元素
    • 1.3 自定义 Map 集合的 key 类型
    • 1.4 LinkedHashMap 类
    • 1.5 TreeMap 类
    • 1.6 Properties 类
    • 1.7 HashMap和 Hashtable的区别
  • 2️⃣ Collections 工具类
  • * 总结

*

1️⃣ 双列对象保存:Map

Collection 每次只能保存一个对象,所以属于单值保存父接口。而在类集中又提供了保存双列对象的集合:Map 集合,利用 Map 集合可以保存一对关联数据 (按照 “key = value”的形式),如下所示,这样就可以实现根据 key 取得 value 的操作。

*

图1Collection 与 Map 保存的区别

下面是Map体系的主要类继承关系:

*

图2Map 体系类继承关系

可以看到,Map接口是所有Map体系类的根接口,其中定义了一些基本的操作方法,如put()get()remove()等;AbstractMap是一个抽象类,它实现了Map接口的骨架实现,提供了一些通用的功能。其他具体的Map类通常继承自它。

而最常用的子类有: 使用哈希表实现的HashMap,它允许使用null作为键或值,并且不保证元素的顺序;基于链表实现的LinkedHashMap,它保持插入顺序或访问顺序(可选); 还有基于红黑树实现的TreeMap,它支持按照键的自然排序进行排序,或者通过Comparator比较器自定义指定顺序。

ConcurrentMap 接口则扩展了Map接口,定义了一些支持并发访问的方法;ConcurrentHashMap 是线程安全的哈希表实现的Map,支持高并发操作,并允许在遍历时进行修改操作。这两个类都是JUC包中的对多线程并发安全做支撑的类,对于这两个类将在我后续的专栏《高并发编程》中详细介绍,敬请期待。

Hashtable则是一个旧版的哈希表实现的Map,线程安全,但性能较低,不推荐使用。它的功能与HashMap类似,但不允许使用null作为键或值。Properties 是一个特殊的Hashtable子类,主要用于处理属性文件。它允许通过键-值对表示配置信息,并提供了一些方法方便地读取和写入这些配置信息。

Map 接口中的常用方法如下所示。

方法名称 描述
int size() 返回Map中键值对的数量
boolean isEmpty() 检查Map是否为空
boolean containsKey(Object key) 检查Map中是否包含指定的键
boolean containsValue(Object value) 检查Map中是否包含指定的值
V get(Object key) 返回指定键对应的值
V put(K key, V value) 将键值对添加到Map
V remove(Object key) 根据键删除Map中对应的键值对
void putAll(Map<? extends K, ? extends V> m) 将另一个Map中的所有键值对添加到当前Map
void clear() 清空Map中的所有键值对
Set<K> keySet() 返回Map中所有键组成的集合
Collection<V> values() 返回Map中所有值组成的集合
Set<Map.Entry<K, V>> entrySet() 返回Map中所有键值对组成的集合
default V getOrDefault(Object key, V defaultValue) 返回指定键对应的值,如果键不存在则返回默认值
default V putIfAbsent(K key, V value) 当指定的键不存在时,将键值对添加到Map

这些方法提供了在Map中添加、获取、删除键值对,以及检查Map的状态和遍历Map的能力。需要根据具体情况选择适当的方法来完成所需的操作。

1.1 HashMap 类与 Hashtable 类

Map 接口中存在两个常用的子类: HashMapHashtable,下面先介绍这两个子类的使用。

//	范例 1: 观察HashMap 子类的使用
package com.xiaoshan.demo;
import java.util.HashMap;
import java.util.Map;

public class TestDemo  {
   
     
	public static void main(String[] args){
   
     
		Map<String, Integer> map = new HashMap<String, Integer>();	//定义Map 集合
		map.put("壹",1);	//保存数据
		map.put("贰",2);
		map.put("叁",3);
		map.put("叁",33);	//key数据重复
		map.put("空",null);	// value为null
		map.put(null,0);	// key为null
		System.out.println(map);	//输出map集合
	}
}	

程序执行结果:

(贰=2, null=0, 叁=33, 壹=1, 空=null)

程序实现了 Map 最为基础的数据保存操作,在实例化 Map 接口对象时首先需要明确地指定泛型类型,此处指定 key 的类型为 String, value 的类型为 Integer, 然后利用 put()方法进行数据的保存。特别需要注意的是,在进行数据保存时,如果出现了key 重复的情况,就会使用新的数据覆盖或替换已有数据。

通过上边范例的操作可以发现 Map 如下特点:

  • 使用HashMap 定义的Map 集合是无序存放的
  • 如果发现了重复的 key 会进行值的覆盖,使用新的内容替换旧的内容
  • 使用HashMap 子类保存数据时 key 或 value 允许保存 null

但需要注意的是,上边范例的代码只是演示了 Map 的基本使用,然而 Map 保存数据的目的并不是进行输出操作,而常常是为了进行查找,即利用 get() 方法通过 key 获取到对应的 value 数据。

//	范例 2: 查询操作
package com.xiaoshan.demo;
import java.util.HashMap;
import java.util.Map;

public class TestDemo {
   
     
	public static void main(String[] args){
   
     
		Map<String, Integer> map =new HashMap<String,Integer>();	// 定义Map 集合
		map.put("壹",1);	//保存数据
		map.put("贰",2);	
		map.put("叁",3);	
		map.put("叁",33);	//key数据重复
		map.put("空",null);	// value为null
		map.put(null,0);	// key为null
		System.out.println(map.get("壹"));	//key存在返回value
		System.out.println(map.get("陸"));	//如果key不存在,返回null
		System.out.println(map.get(null));	// key存在
	}
}

程序执行结果:

1
null
0

此程序利用 Map 接口中的 get()方法根据 key 取得了其对应的 value 内容,通过执行结果可以发现,如果指定的key 存在则会返回与之对应的 value, 而如果 key 不存在则返回 null

通过范例2的代码可以发现,Map 接口在使用中依然只是保存数据与获取数据,那么为什么不直接使用Collection呢,为什么还需要Map接口呢?

首先CollectionMap 接口都可以保存动态长短的数据,然而两者本质的区别在于其使用的环境。Collection接口保存数据的主要目的是输出(利用 Iterator接口)而 Map 保存数据的目的是实现 key查找value的字典功能,虽然Map也可以进行输出操作,但是这样的操作在开发中出现较少。

Map 接口下还有一个 Hashtable 的子类,此类是在JDK 1.0时提供的,属于最早的Map 集合的实现操作。在JDK 1.2时 Hashtable 的子类多实现了一个Map 接口,从而得以保存下来继续使用。

HashtableHashMap 都属于Map接口的子类,所以从本质上讲,它们最终都会利用子类向上转型为Map接口对象实例化。但是在使用Hashtable子类实例化的Map 集合中,保存的keyvalue都不允许出现null,否则会出现"NullPointerException" 异常 。

//	范例 3: 使用 Hashtable
package com.xiaoshan.demo;
import java.util.Hashtable;
import java.util.Map;

public class TestDemo {
   
     
	public static void main(String[] args){
   
     
		Map<String, Integer> map =new Hashtable<String, Integer>();	// 定义Map 集合
		map.put("壹",1);		//保存数据
		map.put("贰",2);
		map.put("叁",3);
		map.put("叁",33);		//key 数据重复
		System.out.println(map.get("壹"));	// key存在返回value
		System.out.println(map.get("陸"));	//key 不存在,返回null
	}
}

程序执行结果:

1
null

此程序利用 Hashtable 子类实例化了 Map 接口,由于接口操作标准统一,所以 put()get()方法都可以正常使用,在使用 get()方法进行数据查找时,如果数据不存在则返回 null

在实际开发中,由于 HashMap 保存数据不受 null 的限制,所以建议优先考虑使用HashMap 子类。

1.2 使用 lterator 获取 Map 集合元素

首先大家必须明确一个开发原则:集合的输出要利用 Iterator 接口完成。但是 Map 接口与 Collection 接口在定义上有所不同, Map 接口并没有提供直接取得 Iterator 接口对象的方法。所以如果要使用 Iterator 输出 Map 接口数据,就必须要清楚 Collection 接口与 Map 接口在数据保存形式上的区别,如下图所示。

*

图3Collection 与 Map 集合数据存储区别

可以发现,当用 Collection 集合保存数据时所有的对象都是直接保存的。而用 Map 集合保存数据时,所保存的 keyvalue 会自动包装为 Map.Entry 接口对象,也就是说如果利用 Iterator 进行迭代,那么每当使用 next()方法读取数据时返回的都是一个 Map.Entry 接口对象,此接口定义如下。

public static interface Map.Entry<K,V>{
   
     }

通过定义可以发现,Map.Entry 接口属于 Map 接口中定义的一个 static 内部接口(相当于外部接口)。Map.Entry 接口定义的常用方法如下所示。

方法 类型 描述
public K getKey() 普通 取得数据中的key
public V getValue() 普通 取得数据中的value
public V setValue(V value) 普通 修改数据中的value

清楚了Map.Entry 接口作用后就可以来研究如何利用 Iterator 接口输出 Map 集合了,在 Map 接口中定义了一个 entrySet()方法:public Set<Map.Entry<K,V>> entrySet(),而实现 Map 接口输出的关键就在于此方法的使用上。

Iterator 输出 Map 集合的操作步骤如下:
(1)利用 entrySet()方法将 Map 接口数据中的数据转换为 Set 接口实例进行保存,此时 Set 接口中所使用的泛型类型为 Map.Entry, 而 Map.Entry 中的 KV 的泛型类型则与 Map 集合定义的 KV 类型相同;
(2)利用Set接口中的 iterator() 方法将Set 集合转化为 Iterator 接口实例;
(3)利用 Iterator 接口进行迭代输出,每一次迭代取得的都是 Map.Entry 接口实例,利用此接口实例可以进行 keyvalue的分离获取。

//	范例 4: 利用 Iterator 实现Map接口的输出
package com.xiaoshan.demo;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class TestDemo {
   
     
	public static void main(String[] args){
   
     
		Map<String, Integer> map = new Hashtable<String,Integer>();// 定义Map 集合
		map.put("壹",1);	//保存数据
		map.put("贰",2);
		map.put("叁",3);
		map.put("叁",33);	//key数据重复
		Set<Map.Entry<String,Integer>> set = map.entrySet();	//将Map集合变为Set集合,目的是使用iterator()方法,注意泛型的统一
		Iterator<Map.Entry<String,Integer>> iter = set.iterator(); 	//取得Iterator实例
		while  (iter.hasNext()){
   
     		//迭代输出
			Map.Entry<String,Integer> entry = iter.next();	//取出Map.Entry
			System.out.println(entry.getKey()+" = "+ entry.getValue());	// 输出数据
		}
	}
}

程序执行结果:

贰 = 2
壹 = 1
叁 = 33

程序按照给出的步骤实现了Iterator 接口输出 Map 集合的操作,其中最为关键的就是 Iterator 每次迭代返回的类型是Map.Entry (注意泛型类型的设置), 而后利用 getKey()getValue()方法才可以取得所保存的 keyvalue数据。

1.3 自定义 Map 集合的 key 类型

在使用Map 接口时可以发现,几乎可以使用任意的类型来作为 keyvalue 的存在,也就表示可以使用自定义的类型作为 key。但要注意的是,作为key 的自定义的类必须要覆写 Object 类中的 hashCode()equals() 两个方法,因为只有靠这两个方法才能确定元素是否重复,也即确认在Map 中能否找到元素,能找到则说明已有相同元素,再根据确认结果进行存储或覆盖等操作。

//	范例 5: 使用自己定义的类作为 Map 集合的key
package com.xiaoshan.demo; 
import java.util.HashMap;
import java.util.Map;

class Book {
   
     		//此类为要保存的 key 类型 
	private String title;		//只定义一个属性
	
	public Book(String title){
   
     		//构造方法接收数据
		this.title = title;
	}
	
	@Override
	public String toString(){
   
     
		return "书名:"+ this.title;
	}
	
	@Override
	public int hashCode(){
   
     	//取得对象编码
		final int prime =31;
		int result =1;
		result = prime* result + (title == null) ? 0 : title.hashCode();
		return result;
	}

	@Override
	public boolean equals(Object obj){
   
     	//进行对象比较
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Book other = (Book) obj;
		if (title == null){
   
     
			if (other.title != null){
   
     
				return false;
			}
		}else if (!title.equals(other.title)){
   
     
			return false;
		}
		return true;
	}
}

public class TestDemo {
   
     
	public static void main(String[] args){
   
     
		Map<Book,String> map = new HashMap<Book,String>();	//实例化Map 接口集合
		map.put(new Book("Java开发"),new String("Java"));        //向Map 接口保存数据
		System.out.println(map.get(new Book("Java开发")));   //根据key 取得value
	}
}

程序执行结果:

Java

此程序使用一个自定义的Book 类作为 Map 集合中的 key 类型,由于已经正确地覆写了 hashCode()equals()方法,因此可以在 Map 集合中根据 key 找到对应的 value数据。

虽然Map集合中可以将各种数据类型作为 key进行设置,但是从实际的开发来讲,不建议使用自定义类作为key,建议使用Java中提供的系统类作为key,如StringInteger等,其中String作为key的情况是最为常见的。

1.4 LinkedHashMap 类

LinkedHashMap是Java中提供的一种基于链表和哈希表实现的有序Map。它继承自HashMap,并添加了一个双向链表用于维护插入顺序或访问顺序。

与普通的HashMap不同,LinkedHashMap会保持元素的插入顺序或访问顺序。具体来说,LinkedHashMap维护了一个双向链表,每个节点都有指向前一个节点和后一个节点的引用。通过这个链表,LinkedHashMap可以按照元素的插入顺序或最近访问的顺序进行迭代。

下面是一个使用LinkedHashMap的代码示例,展示了如何保持元素的插入顺序以及访问顺序:

//	范例 6: LinkedHashMap 保持元素的插入顺序以及访问顺序
package com.xiaoshan.demo;
import java.util.LinkedHashMap;
import java.util.Map;

public class TestDemo {
   
     
	public static void main(String[] args) {
   
     
		// 创建一个具有默认插入顺序的LinkedHashMap
		Map<String, Integer> map = new LinkedHashMap<>();
		// 添加键值对到LinkedHashMap
		map.put("Apple", 10);
		map.put("Banana", 5);
		map.put("Orange", 8);
		// 遍历输出元素,按照插入顺序进行迭代
		System.out.println("按照插入顺序遍历LinkedHashMap:");
		for (Map.Entry<String, Integer> entry : map.entrySet()) {
   
     
			System.out.println(entry.getKey() + ": " + entry.getValue());
		}

		// 创建一个具有自定义访问顺序(访问后放到最后)的LinkedHashMap
		Map<String, Integer> accessOrderedMap = new LinkedHashMap<>(16, 0.75f, true);
		accessOrderedMap.put("Apple", 10);
		accessOrderedMap.put("Banana", 5);
		accessOrderedMap.put("Orange", 8);

		// 遍历输出元素,按照访问顺序进行迭代
		System.out.println("按照访问顺序遍历LinkedHashMap:");
		// 先访问一次"Banana"
		accessOrderedMap.get("Banana");
		// 再遍历输出时会将"Banana"放到最后
		for (Map.Entry<String, Integer> entry : accessOrderedMap.entrySet()) {
   
     
			System.out.println(entry.getKey() + ": " + entry.getValue());
		}
	}
}

程序运行结果:

按照插入顺序遍历LinkedHashMap:
Apple: 10
Banana: 5
Orange: 8
按照访问顺序遍历LinkedHashMap:
Apple: 10
Orange: 8
Banana: 5

可以从结果发现,在默认情况下,LinkedHashMap会保持元素的插入顺序。而如果在构造LinkedHashMap时设置参数accessOrdertrue,则会根据访问顺序进行迭代。当访问一个元素后,该元素会被移到链表的最后面。

1.5 TreeMap 类

TreeMap是Java集合框架中的一种有序映射实现类,它通过红黑树(一种自平衡二叉查找树)来维护键值对的有序性。

TreeMap将键值对按照键的自然排序或者通过指定的Comparator进行排序。默认情况下,使用键的自然顺序进行排序,要求键实现了Comparable接口。如果创建TreeMap时提供了Comparator对象,则会使用该Comparator来排序键。

由于红黑树保持了有序性,TreeMap可实现高效的检索、插入和删除操作。

下面是一个代码示例,演示了TreeMap在默认情况下使用键的自然顺序排序,以及当提供Comparator对象时使用该Comparator进行排序:

//	范例 7: TreeMap在默认情况下使用键的自然顺序排序,当提供Comparator对象时使用该Comparator进行排序
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

class Person implements Comparable<Person> {
   
     
    private String name;

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

    public String getName() {
   
     
        return name;
    }

    @Override
    public int compareTo(Person o) {
   
     
        return this.name.compareTo(o.getName());
    }
}

class ReversedNameComparator implements Comparator<Person> {
   
     
    @Override
    public int compare(Person p1, Person p2) {
   
     
        return p2.getName().compareTo(p1.getName());
    }
}

public class TreeMapSortExample {
   
     
    public static void main(String[] args) {
   
     
        // 默认情况下,TreeMap使用键的自然顺序进行排序
        Map<Person, Integer> naturalOrderMap = new TreeMap<>();
        naturalOrderMap.put(new Person("Alice"), 25);
        naturalOrderMap.put(new Person("Charlie"), 20);
        naturalOrderMap.put(new Person("Bob"), 30);

        System.out.println("自然顺序排序:");
        for (Person person : naturalOrderMap.keySet()) {
   
     
            System.out.println(person.getName() + ": " + naturalOrderMap.get(person));
        }

        // 如果创建TreeMap时提供了Comparator对象,则使用该Comparator进行键的排序
        Map<Person, Integer> customOrderMap = new TreeMap<>(new ReversedNameComparator());
        customOrderMap.put(new Person("Alice"), 25);
        customOrderMap.put(new Person("Charlie"), 20);
        customOrderMap.put(new Person("Bob"), 30);

        System.out.println("\n自定义顺序排序:");
        for (Person person : customOrderMap.keySet()) {
   
     
            System.out.println(person.getName() + ": " + customOrderMap.get(person));
        }
    }
}

在这个示例中,我们首先创建了一个Person类来作为TreeMap的键类型。Person实现了Comparable接口,通过实现compareTo()方法来定义其自然顺序。然后,我们创建了一个ReversedNameComparator类,它实现了Comparator接口,用于自定义键的排序。

接下来,我们分别创建了默认使用自然顺序排序的TreeMap和使用自定义排序的TreeMap。我们将Person对象作为键放入这两个TreeMap中,并将它们遍历输出。

程序运行结果:

自然顺序排序:
Alice: 25
Bob: 30
Charlie: 20

自定义顺序排序:
Charlie: 20
Bob: 30
Alice: 25

可以发现,在默认情况下使用键的自然顺序进行排序。由于Person实现了Comparable接口,可以按照姓名的字典顺序对键进行排序。而在创建TreeMap时提供了Comparator对象时,那么将使用该Comparator来排序键。我们创建了一个ReversedNameComparator对象来反向排序Person的键。

1.6 Properties 类

利用Map 集合可以将任意的数据类型设置为 KeyValue 的类型,虽然这样较为灵活,但是在某些开发中并不适用,所以在类集框架中提供了一个 Properties子类,利用此子类只能保存字符串类型的数据 (key=value)。

Properties 类本身属于 Hashtable 的子类,但是由于 Properties 类都使用 String 数据类型进行操 作,所以在使用Properties类时主要使用本类所定义的方法。 Properties类常用方法如表13-10所示。

方法 描述
Object setProperty(String key,String value) 设置属性
String getProperty(String key) 取得属性,如果key不存在则返回null
String getProperty(String key, String defaultValue) 取得属性,如果key不存在则返回默认值
void store(OutputStream out, String comments) throws IOException 通过输出流保存属性内容,输出的同时可以设置注释信息
void load(InputStream inStream) throws IOException 通过输入流读取属性内容
//	范例 8: 属性的基本操作
package com.xiaoshan.demo;
import java.util.Properties;

public class TestDemo{
   
     
	public static void main(String[] args){
   
     
		Properties pro = new Properties();
		pro.setProperty("BJ","北京");	//保存属性信息
		pro.setProperty("TJ","天津");
		System.out.println(pro.getProperty("BJ"));	//根据key取得属性信息
		System.out.println(pro.getProperty("GZ"));
		System.out.println(pro.getProperty("GZ","没有此记录"));	//没有key返回默认值
	}
}

程序执行结果:

北京
null
没有此记录

此程序是 Properties 类的基本操作,首先进行属性内容的设置,然后根据 key 取得指定的内容。在数据取得时如果有对应的 key 则可以直接输出 value, 如果没有对应的 key 并且设置了默认值,则会输出默认值。

利用Properties类还可以实现属性信息的输出流输出以及输入流读取操作,下面分别介绍这两个操作。

//	范例 9: 将属性信息保存在文件里
package com.xiaoshan.demo;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;

public class TestDemo  {
   
     
	public static void main(String[] args) throws Exception  {
   
     
		Properties pro = new Properties();
		pro.setProperty("BJ","北京");	//保存属性信息
		pro.setProperty("TJ","天津");

		//一般而言后缀可以随意设置,但是标准来讲,既然是属性文件,后缀就必须是*properties,这样做也是为了与国际化对应
		//在进行属性信息保存时如果属性内容为中文则会自动进行转码操作
		pro.store(new FileOutputStream(new File("D:"+ File.separator +"area.properties")),"Area      Info");
	}
}

此程序在属性设置完成后利用 store() 方法将属性内容设置到输出流中,由于使用的是文件输出流,所以最终属性的内容会保存在文件中,同时中文也会自动进行 UNICODE 编码转换。

当本机E 盘上存在了 area.properties 文件,那么就可以利用文件输入流 (FileInputStream) 来进行数据的读取。

//	范例 10: 通过文件流读取属性内容
package com.xiaoshan.demo;
import java.io.File;
import java.io.FilelnputStream;
import java.util.Properties;

public class TestDemo {
   
     
	public static void main(String[] args) throws Exception {
   
     
		Properties pro = new Properties();                                     //实例化类对象
		pro.load(new FileInputStream(new File("E:"+ File.separator+"area.properties")));
		System.out.println(pro.getProperty("BJ");                         //根据key取得value
	}
}

程序执行结果:

北京

此程序利用 load() 方法加载了文件输入流中的属性内容,随后就可以根据 key 取得属性内容了。

在进行属性信息读取时可以发现,前面文章讲解的 ResourceBundle与本节讲解的 Properties 类都支持属性文件(*.properties)的读取,那么在实际开发中使用哪一种呢?

Properties可以读取任意输入流,ResourceBundle 要结合国际化读取 *.prperties 文件。
ResourceBundle类在进行资源文件读取时只能读取后缀为“*.properties”的文件, 并且往往需要通过 Locale类来设置当前国家及语言环境。但是Properties类却可以不区分文件后缀,只要符合它保存数据的结构标准的输入流 (可能通过网络输入或用户输入) 数据都可以进行读取。理论上Properties在读取上更加灵活,但是ResourceBundleLocale类结合读取不同语言资源文件的功能 Properties 类并没有。

所以如果读取国际化资源文件使用 ResourceBundle类,如果读取一些配置信息则可以使用 Properties类。

1.7 HashMap和 Hashtable的区别

Map接口中存在两个常用的子类:HashMapHashtable。尽管它们都是实现Map接口,但它们具有一些区别和特点。

HashMap

  • HashMap是Map接口的最常用实现类之一,它基于哈希表实现;
  • 允许使用null作为键或值;
  • 不保证元素的顺序,即不保证迭代顺序和插入顺序相同;
  • 具有较好的性能,在大多数情况下提供高效的数据存储和检索操作;
  • 非线程安全,若在多线程环境下使用需进行同步处理。

Hashtable

  • Hashtable是早期Java版本提供的哈希表实现的Map类,现在已被HashMap取代,但仍然在一些旧的代码中使用;
  • 不允许使用null作为键或值,否则将抛出NullPointerException;
  • 具有较好的性能,在大多数情况下提供高效的数据存储和检索操作;
  • 通过使用synchronized关键字进行方法级别的同步,使其成为线程安全的实现;
  • 不保证元素的顺序。

需要注意的是,由于Hashtable是线程安全的,可能会在某些应用中造成额外的开销。因此,在单线程环境下,推荐使用HashMap,而在多线程环境下,如需要保证线程安全,可选择使用Hashtable或者ConcurrentHashMap

除了线程安全性和对空键值的处理方式之外,HashMapHashtable在用法和功能上基本相似。因此,大部分情况下,可以优先选择使用HashMap作为Map的实现类。

2️⃣ Collections 工具类

Java 提供类库时考虑到用户的使用方便性,专门提供了一个集合的工具类—— Collections, 这个工具类可以实现List、Set、Map 集合的操作。 Collections类的常用方法如下所示。

方法 描述
boolean addAll(Collection<? super T> c, T... elements) 实现集合数据追加
int binarySearch(List<? extends Comparable<? super T>> list, T key) 使用二分查找法查找集合数据
void copy(List<? super T> dest, List<? extends T> src) 集合复制
void reverse(List<?> list) 集合反转
void sort(List<T> list) 集合排序

通过上面列出的方法可以发现, Collections 提供了一些更为方便的辅助功能操作。下面通过一个简单的范例来进行 Collections工具类的使用验证。

//	范例 11: 为集合追加数据
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TestDemo(
	public static void main(String[] args) throws Exception{
   
     
		List<String> all= new ArrayList<String>();    // 实例化集合对象
		//利用Collections类的方法向集合保存多个数据
		Collections.addAll("xiaoshan","XIAOSHAN","小山","hellojava","HELLOWORLD"); 
		Collections.reverse(all);            //集合反转
		System.out.println(all);          	 //直接输出集合对象
	}
}

程序执行结果:

[HELLOWORLD, hellojava, 小山, XIAOSHAN, xiaoshan]

程序首先实例化了一个空的集合对象,然后利用 Collections 类的 addAll()方法一次性向集合中追加了多条数据,最后又利用 reverse()方法实现了集合数据的反转操作。

需要注意区别的是,Collection 是集合操作的接口,包含List子接口和Set子接口;Collections是集合操作的工具类,可以直接利用类中提供的方法,进行ListSetMap等集合的数据操作。

* 总结

在本文中,我们探讨了双列对象保存的一些关键概念和技术。首先,我们比较了HashMap类和Hashtable类,它们都是常用的Map实现类,但有一些区别,例如线程安全性和对 null值的处理。接下来,我们介绍了使用迭代器(Iterator)来获取Map集合元素的方法,并演示了如何遍历Map中的键值对。然后,我们学习了自定义Map集合的键类型,并说明了为什么重写equals()hashCode()方法对于正确的键值存储至关重要。

我们还介绍了其他一些常见的Map实现类,包括LinkedHashMapTreeMapProperties。最后,我们简要提及了Collections工具类,该工具类提供了一系列静态方法来操作集合,例如排序、查找和同步等。

通过本文的学习,我们对双列对象保存的不同实现和应用有了更深入的了解。根据不同的需求和特点,我们可以选择适合的Map实现类来满足我们的业务要求。同时,Collections工具类也是开发中非常有用的工具,可以提高集合处理的效率和便捷性。

希望本文对读者能够提供有价值的信息,并在开发中对双列对象保存和Collections工具类有所启发。


[* ]nbsp_nbsp 4