集合框架

8. Java集合框架

数据结构概述

数据结构(data structure)是以某种形式将数据组织在一起的集合。数据结构不仅存储数据,还支持访问和处理数据的操作。Java提供了有效组织和操作数据的数据结构。这些数据结构通常称为Java集合框架(java Collection Framwork)。

一个数据结构被认为是一个容器(container),它是一个能存储数据或者元素的对象。定义一种数据结构本质上是定义一个类,数据结构类应该使用数据域存储数据,并提供方法支持查找、插入和删除等操作。 

Java支持两种类型容器:存储一个元素的集合(collection)和存储键/值对的图(map)。

 

集合(collection)

集合有三种:规则集(Set)、线性表(List)和队列(Queue)。Set实例用于存储一组不重复的元素,list实例用于存储由元素构成的有序集合,Queue的实例用于存储用先进先出方式处理的对象。

Collection接口的方法addAll()、removeAll()和retainAll()操作类似于规则集上的并、差和交运算。下图非常经典,便利抽象类是部分实现接口的抽象类。


Collection接口中的有些方法是不能在具体子类中实现的。这种情况这些方法会抛出UnsupportedOperationException异常。


8.1规则集(set)

Set接口扩展了Collection接口。他没有引入新的方法或者常量,只是规定set的实例不包含重复的元素。实现Set的具体类必须确保没有向这个规则集添加重复的元素。

一个规则集的散列码是这个规则集中所有元素散列码的和。Set三个具体类型是:散列类HashSet、链式散列集LinkedSet和树形集TreeSet。 

Iterator接口提供了对不同类型集合中的元素进行统一遍历的统一方法。Collection接口中的iteratorf方法返回Iterator接口的一个实例。next()方法顺序访问集合中的元素,也可以使用hasnext()方法检测迭代器中是否还有更多元素。remove方法删除从迭代器返回的最后一个元素。

for (Object element : set)

         system.out.println(element);

散列集HashSet:

HashSet类可以用来存储互不相同的任何元素。考虑到效率,添加到散列集中的对象必须以一种正确分散散列码的方式来实现hashcode方法。

链式散列集LinkedHashSet:

LinkedHashSet用一个链表实现来扩展HashSet类,它支持对规则集内元素排序。HashSet中元素是没有被排序的,而LinkedHashSet中的元素可以按照他们插入规则集的顺序提取。

Attention:如果不需要维护被插入元素的顺序,应该使用HashSet,它比LinkedHashSet更高效。

树形集TreeSet

SortedSet是Set的一个子接口,它可以确保规则集中的元素是有序的。TreeSet实现了SortedSet接口的一个具体类。

 比较对象的方法:

使用了Comparable接口。由于添加到规则集中的对象都是Comparable的实例,所以可以使用compareTo方法对他们进行比较。这种方法定义的顺序称为自然顺序(nature order)。它属于java.lang包。

 

给规则集指定一个比较器。没有实现Comparable接口或者不想使用该接口,这种定义的顺序称为比较器顺序(order by comparator)。它更灵活,因为可以比较自己定义的新类。他属于java.util。

比较器Comparator接口有两个方法:compare和equals:

public int compare(Object e1. Object e2):

e1小于e2, 返回一个负值。反之返回正值。相等返回0;

public Boolean equals(Object e3)

指定的对象是一个比较器,并且与这个比较器有相同的顺序,返回true。

因为Object类也定义了equals方法。所以,即使在你自定义的比较器类中没有实现equals方法,也不会出现编译错误,但有时候实现该方法可以提高运行效率,让程序快速判断两个比较器是否具有相同的顺序。

通常对于比较器来说,实现Serializable是一个好主意,因为他们可以被用作像TreeSet这样的可序列化数据结构的排序方法。要想使用比较器,必须使用构造方法TreeSet(Comparator comparator)来创建一个有序集,它能使用compare()方法对集合内的元素进行排序。

例一:

import java.util.*;

public class TestTreeSet {
  public static void main(String[] args) {
    // Create a hash set
    Set<String> set = new HashSet<String>();

    // Add strings to the set
    set.add("London");
    set.add("Paris");
    set.add("New York");
    set.add("San Francisco");
    set.add("Beijing");
    set.add("New York");

    TreeSet<String> treeSet = new TreeSet<String>(set);
    System.out.println("Sorted tree set: " + treeSet);

    
  }
}
直接用TreeSet的无参构造创建一个实例,然后再向其中添加,添加一次就要重新排序一次,这样做效率不高。

 

当更新一个规则集时,如果不需要保持元素的排序关系,就应该使用散列集,散列集中插入和删除元素所花的时间较少,效率更高。当需要一个排好序的集合时,可以从这个散列集创建一个树形集。

例二:

What is set1 and set2 after executing set1.addAll(set2);

set1 is [green, red, blue, yellow] 
What is set1 and set2 after executing set1.add(set2); 
 set1 is [green, red, yellow, [red, blue, yellow]] 

8.2线性表(List)

规则集内只能存储不重复的元素。为了允许一个集合中存储重复的元素,需要使用线性表。线性表不仅可以存储重复的元素,而且允许用户指定他们存储的位置。用户可以用下标来访问元素。

List接口扩展了Collection接口,以定义一个允许重复的有序集合。List接口增加了面向位置(position-oriented)的操作,并且增加了一个能够双向遍历线性表的新列表迭代器。

 

线性表的大小是可以动态增大或者减小的。然而数组一旦被创建,它的大小就是固定的。如果不在线性表中插入或者删除元素,那么数组是最高的数据结构。

 

数组线性表(ArryList)和链表类线性表(LinkedList)

ArryList用数组存储元素。这个数组是动态创建的。如果元素个数超过了数组的容量,就创建一个更大的新数组。并将当前数组中的所有元素都复制到新数组中。可以用trimToSize()将数组容量减小到线性表的大小。它更适合于通过下标随机访问元素,但是除了在末尾处之外,不能在其他位置插入或者删除元素,这种情况更高效。

LinkedList在一个链表中存储数据。它适合在线性表的任意位置上插入或者删除元素。

可以用TreeSet在规则集中存储有序的元素,但是线性表不支持有序存储,所以Java集合框架在Collections类中提供了用于对线性表进行排序的静态方法。

 

例三:

What is list1 and list2after executing set1.addAll(list2);
list1is [red, yellow, green, red, yellow, blue]
What is list1 and list2after executing list1.add(list2);
      list1is [red, yellow, green, [red, yellow, blue]]


8.3规则集和线性表总结

规则集比线性表更加高效。如果应用程序用规则集就足够,那就使用规则集,如果对顺序还没有要求,优先考虑使用散列集。

 A simple way to create a set or a list froman array of objects is to use

new HashSet(Arrays.asList(arrayObject)) and

newArrayList(Arrays.asList(arrayObject)). You may also substitute HashSet withLinkedHashSet or TreeSet, and substitute ArrayList with LinkedList.

这个单独一句话是比较对象构成的数组中的最大元素:Collections.max(Arrays.asList(arrayObject))。


8.4向量类Vector和栈类Stack

Java集合框架在Java 2中引入的。之前的版本也支持一些数据结构。比如Vector和Stack类。为了适应Java的集合框架,Java 2对这些类进行了重新设计,为了向后兼容,仍保留了他们所有的旧形式的方法。

除了包含用于访问和修改向量的同步方法外,Vector和ArrayList是一样的。同步方法用于防止两个或则多个线程同时访问某个向量是引起的数据损坏。如果不需要同步的应用程序,使用ArrayList更加高效。

 

8.5队列(Queue)和优先队列(PriorityQueue)

队列是一种先进先出的数据结构。元素被追加到队列末尾,然后从队列头删除。在优先队列中。元素被赋予优先级,当访问元素时,拥有最高优先级的元素首先被删除。

Queue接口用附加的插入、提取和检验操作来扩展Collection接口。

 

Deque扩展了Queue接口,LinkedList类实现了Deque接口,所以可以使用LinkedList创建一个队列。

Deque(double-ended queue)支持在两端插入和删除元素。

 

PriorityQueue类实现了一个优先队列。优先队列使用Comparable以元素的自然顺序进行排序。拥有最小数值的元素被赋予最高优先级,从队列中最早被删除。也可以用Comparator来指定一个顺序。

 

 

图(map)

图是一种依照键值存储元素的容器。键值很像下标,在List中,下标是整数;而在map中,键值可以是任意类型的对象。图中不能有重复的键值,每一个键值对应一个值。在图中存储的是键值以及与键值对应的值构成的条目。

图有三种类型:散列图HashMap、LinkedHashMap和TreeMap。关系如下图:


Map接口提供了查询、更新和获取集合的值和键值的方法。

 

 HashMap、LinkedHashMap和TreeMap类是Map接口的具体实现: 

HashMap;对于定位一个值、插入一个映射和删除一个映射而言,HashMap类是高效的。

 

LinkedHashMap类用链表实现来扩展HashMap类,它支持途中条目的排序。元素既可以按照它们插入图中的顺序排序(插入顺序(insertion order)),也可以按他们被最后一次访问的顺序,从早到晚排序(访问顺序(access order))。

 

TreeMap在遍历排好序的键值时是很高效的。键值可以使用Comparable或者comparator接口来排序。

 

总结:如果更新图时不需要保持图中元素的顺序,就使用HashMap;如果需要保持图中元素的插入顺序或者访问顺序,使用LinkedHashMap;如果需要使图按键值进行排序就要用TreeMap。

 

HashTable: Java2之前,用HashTable来映射键值和元素。为了适应Java集合框架,对HashTable进行了重新设计,并向后兼容了所有的方法。HashTable 实现了Map接口,除了Hashtable具有同步功能之外,它与HashMap的用法是一样的。

 例四:

public class Test{
       public static void main(String args[]){
              Map map = new LinkedHashMap();
              map.put("123", "yansen");
              map.put("111", "wenbo");
              map.put("123", "zhangqiang");
              map.put("222", "zhangqiang");
              System.out.println(map);
       }
      
}

结果:

{123=zhangqiang, 111=wenbo, 222=zhangqiang}。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值