`

关于 Java Collections API 您不知道的 5 件事,第 1 部分(转自IBM)

阅读更多
对于很多 Java 开发人员来说,Java Collections API 是标准 Java 数组及其所有缺点的一个非常需要的替代品。将 Collections 主要与 ArrayList 联系到一起本身没有错,但是对于那些有探索精神的人来说,这只是 Collections 的冰山一角。

关于本系列您觉得自己懂 Java 编程?事实上,大多数程序员对于 Java 平台都是浅尝则止,只学习了足以完成手头上任务的知识而已。在本 系列 中,Ted Neward 深入挖掘 Java 平台的核心功能,揭示一些鲜为人知的事实,帮助您解决最棘手的编程挑战。
.虽然 Map(以及它的常用实现 HashMap)非常适合名-值对或键-值对,但是没有理由让自己局限于这些熟悉的工具。可以使用适当的 API,甚至适当的 Collection 来修正很多易错的代码。

本文是 5 件事 系列 中的第二篇文章,也是专门讨论 Collections 的 7 篇文章中的第一篇文章,之所以花这么大的篇幅讨论 Collections,是因为这些集合在 Java 编程中是如此重要。首先我将讨论做每件事的最快(但也许不是最常见)的方式,例如将 Array 中的内容转移到 List。然后我们深入探讨一些较少人知道的东西,例如编写定制的 Collections 类和扩展 Java Collections API。

1. Collections 比数组好

刚接触 Java 技术的开发人员可能不知道,Java 语言最初包括数组,是为了应对上世纪 90 年代初期 C++ 开发人员对于性能方面的批评。从那时到现在,我们已经走过一段很长的路,如今,与 Java Collections 库相比,数组不再有性能优势。

例如,若要将数组的内容转储到一个字符串,需要迭代整个数组,然后将内容连接成一个 String;而 Collections 的实现都有一个可用的 toString() 实现。

除少数情况外,好的做法是尽快将遇到的任何数组转换成集合。于是问题来了,完成这种转换的最容易的方式是什么?事实证明,Java Collections API 使这种转换变得容易,如清单 1 所示:


清单 1. ArrayToList

import java.util.*;

public class ArrayToList
{
    public static void main(String[] args)
    {
        // This gives us nothing good
        System.out.println(args);
        
        // Convert args to a List of String
        List<String> argList = Arrays.asList(args);
        
        // Print them out
        System.out.println(argList);
    }
} 

注意,返回的 List 是不可修改的,所以如果尝试向其中添加新元素将抛出一个 UnsupportedOperationException。

而且,由于 Arrays.asList() 使用 varargs 参数表示添加到 List 的元素,所以还可以使用它轻松地用以 new 新建的对象创建 List。


--------------------------------------------------------------------------------
回页首
2. 迭代的效率较低

将一个集合(特别是由数组转化而成的集合)的内容转移到另一个集合,或者从一个较大对象集合中移除一个较小对象集合,这些事情并不鲜见。

您也许很想对集合进行迭代,然后添加元素或移除找到的元素,但是不要这样做。

在此情况下,迭代有很大的缺点:

•每次添加或移除元素后重新调整集合将非常低效。
•每次在获取锁、执行操作和释放锁的过程中,都存在潜在的并发困境。
•当添加或移除元素时,存取集合的其他线程会引起竞争条件。
可以通过使用 addAll 或 removeAll,传入包含要对其添加或移除元素的集合作为参数,来避免所有这些问题。


--------------------------------------------------------------------------------
3. 用 for 循环遍历任何 Iterable

Java 5 中加入 Java 语言的最大的便利功能之一,增强的 for 循环,消除了使用 Java 集合的最后一道障碍。

以前,开发人员必须手动获得一个 Iterator,使用 next() 获得 Iterator 指向的对象,并通过 hasNext() 检查是否还有更多可用对象。从 Java 5 开始,我们可以随意使用 for 循环的变种,它可以在幕后处理上述所有工作。

实际上,这个增强适用于实现 Iterable 接口的任何对象,而不仅仅是 Collections。

清单 2 显示通过 Iterator 提供 Person 对象的孩子列表的一种方法。 这里不是提供内部 List 的一个引用 (这使 Person 外的调用者可以为家庭增加孩子 — 而大多数父母并不希望如此),Person 类型实现 Iterable。这种方法还使得 for 循环可以遍历所有孩子。


清单 2. 增强的 for 循环:显示孩子

// Person.java
import java.util.*;

public class Person
    implements Iterable<Person>
{
    public Person(String fn, String ln, int a, Person... kids)
    {
        this.firstName = fn; this.lastName = ln; this.age = a;
        for (Person child : kids)
            children.add(child);
    }
    public String getFirstName() { return this.firstName; }
    public String getLastName() { return this.lastName; }
    public int getAge() { return this.age; }
    
    public Iterator<Person> iterator() { return children.iterator(); }
    
    public void setFirstName(String value) { this.firstName = value; }
    public void setLastName(String value) { this.lastName = value; }
    public void setAge(int value) { this.age = value; }
    
    public String toString() { 
        return "[Person: " +
            "firstName=" + firstName + " " +
            "lastName=" + lastName + " " +
            "age=" + age + "]";
    }
    
    private String firstName;
    private String lastName;
    private int age;
    private List<Person> children = new ArrayList<Person>();
}

// App.java
public class App
{
    public static void main(String[] args)
    {
        Person ted = new Person("Ted", "Neward", 39,
            new Person("Michael", "Neward", 16),
            new Person("Matthew", "Neward", 10));

        // Iterate over the kids
        for (Person kid : ted)
        {
            System.out.println(kid.getFirstName());
        }
    }
}



在域建模的时候,使用 Iterable 有一些明显的缺陷,因为通过 iterator() 方法只能那么 “隐晦” 地支持一个那样的对象集合。但是,如果孩子集合比较明显,Iterable 可以使针对域类型的编程更容易,更直观。


--------------------------------------------------------------------4. 经典算法和定制算法

您是否曾想过以倒序遍历一个 Collection?对于这种情况,使用经典的 Java Collections 算法非常方便。

在上面的 清单 2 中,Person 的孩子是按照传入的顺序排列的;但是,现在要以相反的顺序列出他们。虽然可以编写另一个 for 循环,按相反顺序将每个对象插入到一个新的 ArrayList 中,但是 3、4 次重复这样做之后,就会觉得很麻烦。

在此情况下,清单 3 中的算法就有了用武之地:


清单 3. ReverseIterator

public class ReverseIterator
{
    public static void main(String[] args)
    {
        Person ted = new Person("Ted", "Neward", 39,
            new Person("Michael", "Neward", 16),
            new Person("Matthew", "Neward", 10));

        // Make a copy of the List
        List<Person> kids = new ArrayList<Person>(ted.getChildren());
        // Reverse it
        Collections.reverse(kids);
        // Display it
        System.out.println(kids);
    }
} 


Collections 类有很多这样的 “算法”,它们被实现为静态方法,以 Collections 作为参数,提供独立于实现的针对整个集合的行为。

而且,由于很棒的 API 设计,我们不必完全受限于 Collections 类中提供的算法 — 例如,我喜欢不直接修改(传入的 Collection 的)内容的方法。所以,可以编写定制算法是一件很棒的事情,例如清单 4 就是一个这样的例子:


清单 4. ReverseIterator 使事情更简单

class MyCollections
{
    public static <T> List<T> reverse(List<T> src)
    {
        List<T> results = new ArrayList<T>(src);
        Collections.reverse(results);
        return results;
    }
}



--------------------------------------------------------------------

5. 扩展 Collections API

以上定制算法阐释了关于 Java Collections API 的一个最终观点:它总是适合加以扩展和修改,以满足开发人员的特定目的。

例如,假设您需要 Person 类中的孩子总是按年龄排序。虽然可以编写代码一遍又一遍地对孩子排序(也许是使用 Collections.sort 方法),但是通过一个 Collection 类来自动排序要好得多。

实际上,您甚至可能不关心是否每次按固定的顺序将对象插入到 Collection 中(这正是 List 的基本原理)。您可能只是想让它们按一定的顺序排列。

java.util 中没有 Collection 类能满足这些需求,但是编写一个这样的类很简单。只需创建一个接口,用它描述 Collection 应该提供的抽象行为。对于 SortedCollection,它的作用完全是行为方面的。


清单 5. SortedCollection

public interface SortedCollection<E> extends Collection<E>
{
    public Comparator<E> getComparator();
    public void setComparator(Comparator<E> comp);
}



编写这个新接口的实现简直不值一提:


清单 6. ArraySortedCollection

import java.util.*;

public class ArraySortedCollection<E>
    implements SortedCollection<E>, Iterable<E>
{
    private Comparator<E> comparator;
    private ArrayList<E> list;
        
    public ArraySortedCollection(Comparator<E> c)
    {
        this.list = new ArrayList<E>();
        this.comparator = c;
    }
    public ArraySortedCollection(Collection<? extends E> src, Comparator<E> c)
    {
        this.list = new ArrayList<E>(src);
        this.comparator = c;
        sortThis();
    }

    public Comparator<E> getComparator() { return comparator; }
    public void setComparator(Comparator<E> cmp) { comparator = cmp; sortThis(); }
    
    public boolean add(E e)
    { boolean r = list.add(e); sortThis(); return r; }
    public boolean addAll(Collection<? extends E> ec) 
    { boolean r = list.addAll(ec); sortThis(); return r; }
    public boolean remove(Object o)
    { boolean r = list.remove(o); sortThis(); return r; }
    public boolean removeAll(Collection<?> c)
    { boolean r = list.removeAll(c); sortThis(); return r; }
    public boolean retainAll(Collection<?> ec)
    { boolean r = list.retainAll(ec); sortThis(); return r; }
    
    public void clear() { list.clear(); }
    public boolean contains(Object o) { return list.contains(o); }
    public boolean containsAll(Collection <?> c) { return list.containsAll(c); }
    public boolean isEmpty() { return list.isEmpty(); }
    public Iterator<E> iterator() { return list.iterator(); }
    public int size() { return list.size(); }
    public Object[] toArray() { return list.toArray(); }
    public <T> T[] toArray(T[] a) { return list.toArray(a); }
    
    public boolean equals(Object o)
    {
        if (o == this)
            return true;
        
        if (o instanceof ArraySortedCollection)
        {
            ArraySortedCollection<E> rhs = (ArraySortedCollection<E>)o;
            return this.list.equals(rhs.list);
        }
        
        return false;
    }
    public int hashCode()
    {
        return list.hashCode();
    }
    public String toString()
    {
        return list.toString();
    }
    
    private void sortThis()
    {
        Collections.sort(list, comparator);
    }
} 

这个实现非常简陋,编写时并没有考虑优化,显然还需要进行重构。但关键是 Java Collections API 从来无意将与集合相关的任何东西定死。它总是需要扩展,同时也鼓励扩展。

当然,有些扩展比较复杂,例如 java.util.concurrent 中引入的扩展。但是另一些则非常简单,只需编写一个定制算法,或者已有 Collection 类的简单的扩展。

扩展 Java Collections API 看上去很难,但是一旦开始着手,您会发现远不如想象的那样难。


--------------------------------------------------------------------------------
分享到:
评论

相关推荐

    Java邮件开发Fundamentals of the JavaMail API

    Fundamentals of the JavaMail API Presented by developerWorks, your source for great tutorials ibm.com/developerWorks Table of Contents If you're viewing this document online, you can click ...

    IBM Java英文面试题(附参考答案).doc

    Type 1是JDBC-ODBC桥,Type 2是部分Java驱动,Type 3是纯Java网络协议驱动,Type 4是完全Java的数据库驱动。 26. **如何从客户那里收集需求?** 通常通过访谈、问卷调查、需求分析会议等方式进行,确保了解清楚...

    2022年IBMjava英文面试题.doc

    2022年IBM Java英文面试题 本文档提供了2022年IBM Java英文面试题的详细解释和答案。面试题涵盖了Java基础知识、面向对象编程、集合框架、多线程、网络编程、数据库编程等多方面的知识点。 1. 什么是 Oracle? ...

    IBM java 英文面试题(附参考答案)

    11. **排序与不排序程序**:可以使用Java内置的排序方法,如Arrays.sort()或Collections.sort(),以及自定义排序算法,如快速排序、归并排序等。 12. **遗留系统(Legacy)**:指的是旧的、现有的系统,通常在新...

    java比对升级工具

    4. db2java.jar:可能是IBM DB2数据库的一些Java接口或API,用于操作DB2数据库。 5. commons-collections-3.2.1.jar:Apache Commons Collections库,提供了丰富的集合操作和算法,如查找、过滤、映射等,对于处理...

    JAVA面试题 对JAVA面试有帮助!

     当然这些都是Java的基本题,那些面试的人大多数不会问你Hibernate有多先进,Eclipse的三个组成部分,或command design pattern,他们都是老一辈了,最喜欢问的就是基础知识。别小看了这些基础,我朋友水平一流,...

    JDK1.8 API

    5. **Optional类**:Optional是一个容器类,代表一个值存在或不存在。它避免了空指针异常,鼓励开发者显式处理null值,提高了代码的健壮性。 6. **Date和Time API的改进**:JDK1.8引入了新的日期和时间API,包括...

    jdk-9.0.1_doc-all 最新版

    Defines the Java API for XML Processing (JAXP), the Streaming API for XML (StAX), the Simple API for XML (SAX), and the W3C Document Object Model (DOM) API. java.xml.bind Defines the Java ...

    Java常用的Jar包

    Java开发过程中,jar包是必不可少的资源,它们包含了各种预编译的类库,使得开发者无需从零开始编写代码,可以快速实现特定功能。这里提到的"Java常用的Jar包"集合,包括了多个广泛使用的开源库,每个都有其特定的...

    超级有影响力霸气的Java面试题大全文档

    超级有影响力的Java面试题大全文档 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。...

    Java面试题(20211007014657).pdf

    异常类继承自java.lang.Throwable,分为Error和Exception,Error通常表示系统级错误,Exception用于应用程序中的异常情况。 4. **EJB与Java Bean**:EJB(Enterprise JavaBeans)是服务器端组件,用于构建可部署的...

    java面试题(20211007014846).pdf

    1. **J2EE**:Java 2 Platform, Enterprise Edition,是Java平台的企业版,主要用于构建和部署分布式企业级应用,包括Web服务、应用程序服务器、JavaBeans和EJB等。 2. **Collection Framework**:Java集合框架提供...

    Java开发中常用jar包的作用

    在Java开发中,有许多常用的第三方jar包为开发者提供了丰富的功能,极大地提高了开发效率和程序质量。以下是一些常见jar包及其用途的详细介绍: #### activation.jar - **作用**: 与`javaMail`相关的jar包,主要...

    worklightserver部署在tomcat下需要添加的jar

    工作轻量级服务器(Worklight Server)是一款IBM提供的企业级移动应用开发和管理平台,它允许开发者构建、测试、监控和部署多平台的移动应用程序。将Worklight Server部署到Apache Tomcat服务器上是一项常见的任务,...

    kettle 4.3依赖的jar

    首先,JDBC(Java Database Connectivity)是Java中用于与数据库进行通信的标准API。在Kettle 4.3中,JDBC驱动程序是必不可少的,因为它们允许Kettle连接到各种类型的数据库,如MySQL、Oracle、SQL Server、...

    程序员成长学习要求

    4. **集合框架**:深入理解`List`、`Set`、`Map`以及`Collections`类的功能与用法,这些是Java编程中最常用的集合类型。 5. **Java版本特性**: - **Java 6/Java 7/Java 8**:对比不同版本之间的差异,特别是Java ...

    wsrr类型实例

    这一过程可以通过编程接口(API)实现,例如使用Java API或RESTful API。下面,我们将详细讨论这些步骤,并通过代码示例来展示具体操作。 1. 创建服务定义: 使用WSRR的API,开发者可以创建一个服务定义对象,该...

    mongodb的eclipse插件

    1. 图形化查询构建器:对于不熟悉MongoDB查询语法的用户,插件提供了图形化的查询构建界面,可以通过拖拽和设置条件来生成查询语句。 2. 文档编辑器:支持直接在Eclipse中编辑和保存MongoDB文档,方便对单个文档...

    介绍J2EE常用Jar包的含义.doc

    - **应用场景**: 需要动态更新网页部分内容而不重新加载整个页面的应用场景,例如实时聊天系统中的消息列表更新。 3. **ant.jar** 和 **antlr.jar** - **用途**: `ant.jar`是Apache Ant的一部分,用于构建Java...

Global site tag (gtag.js) - Google Analytics