`

Java集合框架(一)--集合API与数据结构的关系

阅读更多
Java的集合框架是对常用数据结构的实现,Java程序员每天都会用到集合框架,但是你对它真的了解吗?我依旧记得我第一份工作中我的同事问我为什么在HashMap中取得数据没有按照存数据的顺序拿出来,而是乱序的,怎么对HashMap进行排序啊?如果他对集合框架足够了解的话,就会知道使用LinkedHashMap可以维持存入数据的顺序,使用TreeMap存入数据时就已经对TreeMap里的数据排好序了。

本文的主要内容包括:(分两篇)
一、 常用的集合实现类与数据结构的关系
二、 Java8新增的函数式集合操作方式

一、常用的集合实现类与数据结构的关系

先看一下Java集合框架的类图



把集合框架中继承关系的抽象类去掉了,紫色的是接口,白色的是具体实现类。任何时候都应该优先考虑使用接口而不是实现类编程,除非接口中不包含需要的方法。

Iterator代表可以直接迭代的集合,Map并没有提供直接迭代的操作,所以Map被设计成与Iterator没有继承关系。

按照继承关系可以分为两大类,那么
1、集合元素为对象的Collection集合,
2、集合元素为键值对的Map集合。

若是按照实现数据结构的类型来分,那么
1、Queue接口代表的是队列,其子接口Deque代表的是双端队列,实际上Deque既包含了队列的方法也包含了栈的方法。
2、List接口代表的是有序列表
3、Set接口代表的是无序列表
4、Map接口代表是关联数组

下面接着说具体的实现类:
1、 PriorityQueue是对优先队列的实现,也就是,插入PriorityQueue的元素默认会按照自然顺序从小到大排序,例如:
Queue<Integer> pq = new PriorityQueue<Integer>();
		pq.offer(4);
		pq.offer(3);
		pq.offer(5);
		System.out.println("遍历PriorityQueue:");
		for (Integer e : pq) {
			System.out.println(e);
		}
System.out.println("从PriorityQueue中取数据:");
		while(!pq.isEmpty()){
			System.out.println(pq.poll());
		}


offer()方法是往队列尾部插入一个元素,与add()方法是一样的,实际上add()方法的实现就是直接调用offer()方法而已。使用Poll()方法每次取数据都会按照队列的方式取集合中第一个数据,并且把取了的元素删掉。上面这段代码的执行结果如下:



当然了,这个插入元素时的排序规则我们也是可以自定义的,只要在构造PriorityQueue对象是传入Comparator比较器接口的实现对象就可以了,或者保证插入PriorityQueue的元素均实现了Comparable接口。因为PriorityQueue在实际编程中用的似乎不多,比较器的用法请参考HashSet与HashMap部分。

2、 ArrayDeque是对双端队列的数组实现
前面说了,双端队列既可以当做队列使用也可以当做栈来使用,看代码实现:
Deque<Integer> ad = new ArrayDeque<Integer>();
		System.out.println("将ArrayDeque当做队列使用:(先进先出)");
		ad.offer(4);
		ad.offer(3);
		ad.offer(5);
		while(!ad.isEmpty()){
			System.out.println(ad.poll());
		}
		System.out.println("将ArrayDeque当做栈使用:(后进先出)");
		ad.push(5);
		ad.push(3);
		ad.push(4);
		while(!ad.isEmpty()){
			System.out.println(ad.pop());
		}


上面这段代码执行结果如下:



3、 ArrayList应该是我们用的最多的集合类了,对应数据结构里的顺序表的概念,一组地址连续的存储单元构成的集合。基于数据实现,但是多了自动扩容的功能,其实相当于一个变长数组。不过Java中数组创建时长度就定了,所谓的自动扩容其实是新建了一个新数组,然后将原来数组的值复制到新数组里面。你可能会认为ArrayList的自动扩容效率很低,其实ArrayList赋值数组的方法使用的是Java虚拟机实现的本地方法,直接操作内存空间,并不是一个个元素遍历来复制的。

当你需要复制数组时,也可是直接使用Arrays.copyOf()方法,或者System.copyArray()方法,效率比遍历快多了。ArrayList就是用这个方式来复制的。
创建ArrayList是若没有指定数组长度则默认数组长度为10,每次新增元素的相关操作都会检查是否还有数组长度是否已经用完了,若用完了,则创建一个新的数组,长度比旧的数组增长二分之一。即一次扩容后长度为15,第二次为22,以此类推。。。
Java程序员每天都在用的ArrayList类除了自动扩容这个特性,没什么好说的。

4、 LinkedList,List接口的链表实现,同时实现了Deque接口,也就是说LinkedList既可以当成普通的链表使用,也可以当成队列和栈来使用。就效率而言,链表的随机插入和删除效率是高于数组的,而随机访问和修改效率则低于数组。这也是LinkedList与ArrayList的差别。至于栈和队列的使用方法,与上面ArrayDeque演示的一致,就不重复说代码了。

Set集合与Map集合关系很密切,因为Set集合就是用Map集合实现的。在JDK的实现中,Set集合里是Map集合中Value为一个常量对象的集合。这个常量的代码如下所示。
private static final Object PRESENT = new Object();


Set集合里面持有了一个Map集合的引用,比如HashMap里面有一个HashMap的引用,然后再HashSet的构造方法里面实例化了一个对应的HashMap。
private transient HashMap<E,Object> map;


Set集合的所有操作都会委托给对应的Map去实现。
所以Set集合与Map集合可以放在一起讨论。

5、 HashSet与HashMap,对应数据结构里面的哈希表,或者叫散列表。根据HashMap的key对应的hashcode进行寻址,然后就可以根据key值映射的value的值了。若是不考虑元素的存取顺序,任何时候都应该优先考虑HashSet和HashMap。毕竟哈希表在所有的数据结构中是效率最高的。

6、 LinkedHashSet与LinkedHashMap,在使用哈希表作为数据结构的同时维护了一个链表,这个链表记录插入数据的顺序,所以我们再遍历的时候就可以得到与插入数据顺序一致的数据了。而其余操作是使用哈希表的数据结构,这样保证了在读取元素、插入与删除数据时的高效。

7、 TreeSet与TreeMap,使用红黑树的数据结构实现,在插入和删除数据时保证了树结构的平衡,这样在插入删除数据等操作时的效率以稳定的。红黑树是一种特殊的二叉搜索树,这是一种排序树,所以在需要对Set和Map元素进行排序时,优先考虑TreeSet和TreeMap。默认情况下会以自然顺序对Key从小到大进行排序。

这里需要注意的是,有两种方法可以设置TreeSet和TreeMap的比较规则,一种是构造TreeSet和TreeMap是传入自己的Comparator实现类,另一种是对插入TreeSet或TreeMap的key元素实现了Comparable接口。

幸好,常用的基本类型和String类已经实现类Comparable接口,它们的比较规则是自然排序规则。

若是自己定义的类没有实现Comparable接口,同时在TreeSet和TreeMap时也没有传入Comparator实现类的对象,编译时不会报错,但是在运行时会跑出ClassCastException,这是需要注意的地方。

下面以TreeMap为例,使用自定义Key类型,分别介绍使用Comparator接口和使用Comparable接口作为比较器的用法。

1)使用Comparator接口
代码如下:
public class TreeMapTest {
	public static void main(String[] args) {
		Map<KeyTest, Object> tm = new TreeMap<KeyTest, Object>(new KeyComparator<KeyTest>());
		tm.put(new KeyTest(4), "4");
		tm.put(new KeyTest(3), "3");
		tm.put(new KeyTest(5), "5");
		for (Entry<KeyTest, Object> entry : tm.entrySet()) {
			System.out.println(entry.getKey().getValue() + "-->" + entry.getValue());
		}
	}
}

class KeyTest{
	int value;

	public KeyTest(int value){
		this.value = value;
	}
	
	public int getValue() {
		return value;
	}
	
}

class KeyComparator<T extends KeyTest> implements Comparator<T>{

	/**
	 * o1<o2返回-1,o1>o2返回1,o1=o2返回0,这是从小到大排序,反之则从大到小排序
	 */
	@Override
	public int compare(T o1, T o2) {
		if(o1.getValue() < o2.getValue()){
			return -1;
		} else if(o1.getValue() > o2.getValue()){
			return 1;
		}
		return 0;
	}
	
}
上面这段代码执行结果如下:



2)使用Comparable接口
代码如下:
public class TreeMapTest {
	public static void main(String[] args) {
		Map<KeyTest, Object> tm = new TreeMap<KeyTest, Object>();
		tm.put(new KeyTest(4), "4");
		tm.put(new KeyTest(3), "3");
		tm.put(new KeyTest(5), "5");
		for (Entry<KeyTest, Object> entry : tm.entrySet()) {
			System.out.println(entry.getKey().getValue() + "-->" + entry.getValue());
		}
	}
}

class KeyTest implements Comparable<KeyTest>{
	int value;

	public KeyTest(int value){
		this.value = value;
	}
	
	public int getValue() {
		return value;
	}

	/**
	 * this.value<o返回-1,this.value>o返回1,this.value=o返回0,这是从小到大排序,反之则从大到小排序
	 */
	@Override
	public int compareTo(KeyTest o) {
		if(this.value < o.getValue()){
			return -1;
		} else if(this.value > o.getValue()){
			return 1;
		}
		return 0;
	}
	
}
上面这段代码执行结果如下:



建议总是优先考虑使用Comparator接口的实现类作为比较器,这样不会将比较器的实现代码侵入到JavaBean中。

8、EnumSet和EnumMap,枚举元素在集合中的体现

前面说到常用的Set集合使用Map集合实现的,但是就一个例外,就是EnumSet和EnumMap集合。EnumSet里面并没有持有EnumMap的引用。
示例代码中用到的枚举类如下:
enum EnumType{
		TEST("test"),JDBC("jdbc"),SPRING("spring"),HIBERNATE("hibernate");
		
		private final String value;
		
		private EnumType(String value){
			this.value = value;
		}

		public String getValue() {
			return value;
		}
		
	}


EnumSet是抽象类,提供了一个工厂方法EnumSet.noneOf()来实例化对象。在
EnumSet.noneOf()方法实现中,当提供的参数的枚举类型的元素少于64个时,使用RegularEnumSet实现类实例化EnumSet,否则使用JumboEnumSet实现类实例化EnumSet对象。特别注意的是,EnumSet并没有直接提供插入元素的add()方法,而是使用多个重载的of()方法直接构造基于泛型参数类型的枚举元素,一旦of()方法调用完,就不能往EnumSet里面插入元素了。若想使用add()方法插入枚举元素到EnumSet中,则只能使用EnumSet的子类了。

另外EnumSet还提供了一个拷贝构造方法和拷贝方法copyOf(),用于将现有的EnumSet对象拷贝到新对象中。

EnumSet的用法如以下代码所示:
System.out.println("EnumSet的of()方法:");
		EnumSet<EnumType> enumSet = EnumSet.of(EnumType.JDBC, EnumType.SPRING);
		for (EnumType enumType : enumSet) {
			System.out.println(enumType + "-->" + enumType.getValue());
		}
		System.out.println();
		System.out.println("EnumSet的allOf()方法:");
		enumSet = EnumSet.allOf(EnumType.class);
		for (EnumType enumType : enumSet) {
			System.out.println(enumType + "-->" + enumType.getValue());
		}
上一段代码的执行结果如下:



EnumMap是具体实现类,直接用构造方法实例化对象。EnumMap是只能以实例化对象时传入的枚举参数的元素作为Key的Map,若插入元素时传入的Key参数不是枚举参数的元素,则会抛出ClassCastException异常。

EnumMap的用法如下:
EnumMap<EnumType,Object> enumMap = new EnumMap<EnumType, Object>(EnumType.class);
		enumMap.put(EnumType.JDBC, "jdbc元素");
		enumMap.put(EnumType.TEST, "test元素");
		for (Map.Entry<EnumType, Object> map : enumMap.entrySet()) {
			System.out.println(map.getKey()+"(\""+map.getKey().getValue()+"\")"+ "-->" + map.getValue());
		}
上一段代码的执行结果如下:




  • 大小: 78.4 KB
  • 大小: 9.9 KB
  • 大小: 14.9 KB
  • 大小: 2.9 KB
  • 大小: 2.9 KB
  • 大小: 16.2 KB
  • 大小: 6.9 KB
0
0
分享到:
评论

相关推荐

    数据结构和Java集合框架

    数据结构和Java集合框架是Java编程中至关重要的概念,它们是高效编程和算法设计的基础。在Java中,数据结构指的是组织、存储和管理数据的方式,而集合框架则是一组接口和类,为处理各种数据结构提供了统一的API。 ...

    Java集合框架pdf--培训中心资料

    Java集合框架是一组用于存储和操作数据的API,提供了多种数据结构和算法来帮助开发者高效地管理和处理数据。集合框架主要包括`Collection`和`Map`两个顶层接口,以及它们的子接口和实现类。 **1. Collection接口** ...

    Java集合框架常见面试题.pdf

    总结来说,Java集合框架为开发者提供了丰富的数据结构以应对不同场景的需要,从简单的List和Set到复杂的Map结构,再到线程安全的集合实现,每个组件都有其特定的用途和优势。在面试中,理解并能够熟练运用这些集合类...

    java api文档jdk-6u30-apidocs

    `java.util`提供了一组通用的数据结构和算法,如集合框架、日期和时间处理。`java.io`则涉及输入/输出操作,包括文件读写、流处理等。 2. **集合框架**:Java API 6u30中的集合框架是一个强大的工具,它允许开发者...

    JAVA 编程 API基础 JAVA开发平台,JAVA编程资源----JAVA API.zip

    `java.util`提供了数据结构和算法,如集合框架(ArrayList、LinkedList、HashMap等)、日期时间处理(Date、Calendar)以及泛型等。`java.io`负责输入输出操作,如文件读写、流处理。`java.net`则处理网络通信相关...

    数据结构和Java集合框架 英文版 第三版

    ### 数据结构与Java集合框架知识点概述 #### 一、数据结构基础 1. **定义**: - 数据结构是计算机科学中的一个基本概念,指在计算机中存储、组织数据的方式。 - 它不仅涉及数据如何存储,还包括数据之间如何关联...

    Java程序员集合框架面试题-java集合框架面试题.docx

    Java集合框架是Java编程语言中的核心部分,它提供了一组高效的数据结构和算法,使得开发者能够方便地管理和操作数据。面试中,对于Java程序员来说,集合框架是必考的知识点,因为它涉及到程序的基础架构和数据处理...

    java基础 集合-21-对象的一对多与多对多

    Java集合框架提供了多种数据结构供选择,根据实际需求可以选择适当的数据结构。同时,为了保持数据的一致性,可能需要在“一”端添加删除操作时同步更新“多”端的集合。 3. **对象的多对多关系**: 多对多关系...

    Java集合框架使用总结

    ### Java集合框架使用总结 #### 前言 本文旨在为读者提供关于Java集合框架的概览性...通过对Java集合框架的这些核心概念的理解,开发者能够更好地选择合适的集合类型以适应不同的应用场景,并有效地管理数据结构。

    学士后Java集合框架和泛型课后习题答案

    Java集合框架是Java编程语言中的一个核心组成部分,它为数据存储和操作提供了丰富的类库。在Java中,集合框架主要包括接口(如List、Set、Queue)和实现这些接口的类(如ArrayList、HashSet、LinkedList等)。这个...

    Java集合框架(JCF:Java Collections Framework)之概述

    Java 集合框架的优点包括:减少了编程的辛劳,提高了程序的速度和质量,鼓励软件的复用,减少了设计新 API 的努力,减少了学习和使用新的 API 的辛劳。 Java 集合框架的出现,使得程序员可以更方便地使用集合,提高...

    Java 集合框架(1-9)-Collection 类关系图.pdf

    Java集合框架是Java编程语言中的一个核心组成部分,它提供了一套通用的数据结构和容器,用于存储和管理对象。集合框架自JDK 1.2引入以来,极大地提升了开发效率,优化了程序性能,并增强了不同API之间的互操作性。在...

    Java-collection-frame.rar_Java集合框架

    Java集合框架是Java编程语言中一个至关重要的组成部分,它为数据存储、管理和操作提供了丰富的类库。这个框架包括了各种接口、类以及实现,使得开发者能够高效地处理对象的集合,无论是小型还是大型数据集。在Java...

    Java集合框架培训资料

    Java集合框架是Java编程语言中的核心组件之一,它为数据存储和管理提供了丰富的类和接口。这个培训资料将深入探讨Java集合框架的各个方面,帮助开发者更有效地利用这些工具。 首先,我们要了解Java集合框架的基本...

    java集合框架全面进阶

    Java集合框架是Java编程语言中不可或缺的一部分,它提供了一组接口和类,用于高效地存储、管理和操作数据。本篇文章将深入探讨Java集合框架的各个方面,帮助开发者从基础到高级全面掌握这一关键知识。 首先,我们要...

    Java集合框架.ppt

    总的来说,Java集合框架通过提供标准化的数据结构(如List、Set和Map)和算法,简化了数据的存储和处理,提高了程序的效率和质量。理解和熟练掌握这个框架是每个Java开发者的必备技能,对于编写高效、灵活的代码至关...

    Java API 文档 jdk-17.0.2-doc-all

    2. **集合框架**:`java.util`包中的集合框架是Java编程的核心部分,包括`ArrayList`、`LinkedList`、`HashMap`、`HashSet`等数据结构。了解它们的特点、用法和性能是每个Java开发者的基本功。 3. **输入/输出流**...

    02-Java集合框架(外发).pdf

    - **概念**:Java集合框架是一个用于存储和操作对象集合的API,它提供了多种数据结构以适应不同的场景需求。 - **目的**:简化程序员的工作,提供高效的数据管理方式。 #### 二.`Collection`接口 - **定义**:`...

    java集合框架PPT

    Java集合框架是Java编程语言中一个至关重要的部分,它为数据存储和操作提供了丰富的类库。这个PPT作为培训教材,旨在深入解析Java集合框架的核心概念、接口和类,帮助学习者掌握其基本使用和高级特性。以下是Java...

Global site tag (gtag.js) - Google Analytics