`

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 集合框架介绍.ppt

    “集合框架”...对于计算机科学数据结构课程中学到的许多抽象数据类型如映射(map)、集(set)、列表(list)、树(tree)、数组(array)、散列表(hashtable)和其它集合来说,该框架提供了一个方便的 API

    JAVA_API1.6文档(中文)

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    Java 1.6 API 中文 New

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    202321-数据结构与算法面试题(2023最新版)思维导图.zip

    - 集合框架 - 异常处理 - 多线程编程 2. 数据库: - 熟悉SQL语言 - 了解关系型数据库和非关系型数据库 - 数据库连接池 - 数据库事务 3. Spring框架: - Spring Boot - Spring MVC - Spring Data - ...

    Java集合容器面试题(2023最新版)-重点

    集合框架通过提供有用的数据结构和算法使你能集中注意力于你的程序的重要部 分上,而不是为了让程序能正常运转而将注意力于底层设计上。 通过这些在无关API之间的简易的互用性,使你免除了为改编对象或转换代码以 ...

    java8集合源码分析-Awesome-Java:真棒-Java

    Java集合框架,并发容器,同步容器等 IO框架 Java基础字节流 字符流 NIO等 Java8 Java8语言的行为参数化和流编程等 Java虚拟机 Java虚拟机相关,内存模型,类加载机制,JVM性能解析等 零散的文章 数据结构与算法 ...

    Trie-APIs-In-Java-With-Tests:与Trie数据结构相关的API集合,以Java实现并得到强大的测试支持

    带有测试的Java中的Trie API 与Trie数据结构相关的API集合,以Java实现,并经过严格的测试支持,以及一个示例问题,该问题通过通过(依赖项注入框架)向其中注入Trie服务来使用Trie。使用的框架/库- -Google的依赖...

    java api最新7.0

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    JavaAPI1.6中文chm文档 part1

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    JavaAPI中文chm文档 part2

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    common-collection4.2jar包及API文档

    在Java集合框架 是JDK 1.2的一大补充。它添加了许多强大的数据结构,可加速最重要的Java应用程序的开发。从那时起,它已成为Java中集合处理的公认标准。 Commons-Collections通过提供新的接口,实现和实用程序来...

    play framework 框架手册 word 版

    支持类型的数组或集合 - 28 - POJO对象绑定 - 29 - JPA 对象绑定 - 30 - 定制绑定 - 30 - @play.data.binding.As - 30 - @play.data.binding.NoBinding - 31 - play.data.binding.TypeBinder - 31 - @play.data....

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    全书分4篇,共24章,其中,第1篇为技能学习篇,主要包括Java Web开发环境、JSP语法、JSP内置对象、Java Bean技术、Servlet技术、EL与JSTL标签库、数据库应用开发、初识Struts2基础、揭密Struts2高级技术、Hib锄劬e...

    eclipse-collections:Eclipse Collections是Java的collections框架,具有优化的数据结构和丰富,功能强大且流畅的API

    Eclipse Collections是一个用于Java的综合集合库。 该库通过提供一组富有表现力和高效的API和类型来提高生产力和性能。 迭代协议受Smalltalk收集框架的启发,并且这些收集与Java收集框架类型兼容。 Eclipse ...

    ebook-library-java-spring:演示后端api应用程序,用于使用Java和Spring框架以及Poastgres的电子书库集合

    使用Java和Spring引导框架的基本演示项目 到目前为止使用的依赖项 Postgres数据库驱动程序 资源资源 初始Spring结构形式。 一些文件 未来的计划 使用以下数据开始为电子书图书馆项目添加类,服务和存储库: : 开始...

    java开源包11

    使用redis作缓存时,支持list类型的高级数据结构,更适合论坛帖子列表这种类型的数据 5. 支持混合使用redis缓存和memcached缓存。可以将列表数据缓存到redis中,其他kv结构数据继续缓存到memcached 6. 支持redis的...

    java开源包4

    使用redis作缓存时,支持list类型的高级数据结构,更适合论坛帖子列表这种类型的数据 5. 支持混合使用redis缓存和memcached缓存。可以将列表数据缓存到redis中,其他kv结构数据继续缓存到memcached 6. 支持redis的...

    play框架手册

    支持类型的数组或集合 - 28 - POJO对象绑定 - 29 - JPA 对象绑定 - 30 - 定制绑定 - 30 - @play.data.binding.As - 30 - @play.data.binding.NoBinding - 31 - play.data.binding.TypeBinder - 31 - @play.data....

Global site tag (gtag.js) - Google Analytics