`

Java Collections API 您不知道的 5 件事

    博客分类:
  • J2SE
阅读更多

 

简介:  Java™ Collections API 远不止是数组的替代品,虽然一开始这样用也不错。 Ted Neward 提供了关于用 Collections 做更多事情的 5 个技巧,包括关于定制和扩展 Java Collections API 的基础。

: 此文摘自 IBM 文档库 .

 

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 中,但是 34 次重复这样做之后,就会觉得很麻烦。

在此情况下,清单 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 Serialization 一样,Java Collections API 还有很多角落等待有人去探索 正因为如此,我们还不准备结束这个话题。在 5 件事 系列 的下一篇文章中,将可以看到用 Java Collections API 做更多事情的 5 种新的方式。

1. List 不同于数组

Java 开发人员常常错误地认为 ArrayList 就是 Java 数组的替代品。Collections 由数组支持,在集合内随机查找内容时性能较好。与数组一样,集合使用整序数获取特定项。但集合不是数组的简单替代。

要明白数组与集合的区别需要弄清楚顺序位置 的不同。例如,List 是一个接口,它保存各个项被放入集合中的顺序 ,如清单 1 所示:


清单 1. 可变键值

                              

import java.util.*;

 

public class OrderAndPosition

{

    public static <T> void dumpArray(T[] array)

    {

        System.out.println("=============");

        for (int i=0; i<array.length; i++)

            System.out.println("Position " + i + ": " + array[i]);

    }

    public static <T> void dumpList(List<T> list)

    {

        System.out.println("=============");

        for (int i=0; i<list.size(); i++)

            System.out.println("Ordinal " + i + ": " + list.get(i));

    }

   

    public static void main(String[] args)

    {

        List<String> argList = new ArrayList<String>(Arrays.asList(args));

 

        dumpArray(args);

        args[1] = null;

        dumpArray(args);

       

        dumpList(argList);

        argList.remove(1);

        dumpList(argList);

    }

}

 

当第三个元素从上面的 List 中被移除时,其 后面 的各项会上升填补空位。很显然,此集合行为与数组的行为不同(事实上,从数组中移除项与从 List 中移除它也不完全是一回事儿 从数组中 移除 项意味着要用新引用或 null 覆盖其索引槽)。


2. 令人惊讶的 Iterator

无疑 Java 开发人员很喜爱 Java 集合 Iterator ,但是您最后一次使用 Iterator 接口是什么时候的事情了?可以这么说,大部分时间我们只是将 Iterator 随意放到 for() 循环或加强 for() 循环中,然后就继续其他操作了。

但是进行深入研究后,您会发现 Iterator 实际上有两个十分有用的功能。

第一,Iterator 支持从源集合中安全地删除对象,只需在 Iterator 上调用 remove() 即可。这样做的好处是可以避免 ConcurrentModifiedException , 这个异常顾名思意:当打开 Iterator 迭代集合时,同时又在对集合进行修改。有些集合不允许在迭代时删除或添加元素,但是调用 Iterator remove() 方法是个安全的做法。

第二,Iterator 支持派生的(并且可能是更强大的)兄弟成员。ListIterator , 只存在于 List 中,支持在迭代期间向 List 中添加或删除元素,并且可以在 List 中双向滚动。

双向滚动特别有用,尤其是在无处不在的 滑动结果集 操作中,因为结果集中只能显示从数据库或其他集合中获取的众多结果中的 10 个。它还可以用于 反向遍历 集合或列表,而无需每次都从前向后遍历。插入 ListIterator 比使用向下计数整数参数 List.get() “ 反向 遍历 List 容易得多。


3. 并非所有 Iterable 都来自集合

Ruby Groovy 开发人员喜欢炫耀他们如何能迭代整个文本文件并通过一行代码将其内容输出到控制台。通常,他们会说在 Java 编程中完成同样的操作需要很多行代码:打开 FileReader ,然后打开 BufferedReader , 接着创建 while() 循环来调用 getLine() ,直到它返回 null 。 当然,在 try/catch/finally 块中必须要完成这些操作,它要处理异常并在结束时关闭文件句柄。

这看起来像是一个没有意义的学术上的争论,但是它也有其自身的价值。

他们(包括相当一部分 Java 开发人员)不知道并不是所有 Iterable 都来自集合。Iterable 可以创建 Iterator ,该迭代器知道如何凭空制造下一个元素,而不是从预先存在的 Collection 中盲目地处理:


清单 2. 迭代文件

                              

// FileUtils.java

import java.io.*;

import java.util.*;

 

public class FileUtils

{

    public static Iterable<String> readlines(String filename)

        throws IOException

    {

        final FileReader fr = new FileReader(filename);

        final BufferedReader br = new BufferedReader(fr);

       

        return new Iterable<String>() {

               public <code>Iterator</code><String> iterator() {

                       return new <code>Iterator</code><String>() {

                               public boolean hasNext() {

                                      return line != null;

                               }

                               public String next() {

                                      String retval = line;

                                      line = getLine();

                                       return retval;

                               }

                               public void remove() {

                                      throw new UnsupportedOperationException();

                               }

                               String getLine() {

                                      String line = null;

                                      try {

                                              line = br.readLine();

                                      }

                                      catch (IOException ioEx) {

                                              line = null;

                                      }

                                      return line;

                               }

                               String line = getLine();

                       };

               }      

        };

    }

}

 

//DumpApp.java

import java.util.*;

 

public class DumpApp

{

    public static void main(String[] args)

        throws Exception

    {

        for (String line : FileUtils.readlines(args[0]))

            System.out.println(line);

    }

}

 

此方法的优势是不会在内存中保留整个内容,但是有一个警告就是,它不能 close() 底层文件句柄(每当 readLine() 返回 null 时就关闭文件句柄,可以修正这一问题,但是在 Iterator 没有结束时不能解决这个问题)。


4. 注意可变的 hashCode()

Map 是很好的集合,为我们带来了在其他语言(比如 Perl )中经常可见的好用的键/ 值对集合。JDK HashMap 的形式为我们提供了方便的 Map 实现,它在内部使用哈希表实现了对键的对应值的快速查找。但是这里也有一个小问题:支持哈希码的键依赖于可变字段的内容,这样容易产生 bug ,即使最耐心的 Java 开发人员也会被这些 bug 逼疯。

假设清单 3 中的 Person 对象有一个常见的 hashCode() (它使用 firstNamelastName age 字段 所有字段都不是 final 字段 计算 hashCode() ),对 Map get() 调用会失败并返回 null


清单 3. 可变 hashCode() 容易出现 bug

                              

// 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 kid : kids)

            children.add(kid);

    }

   

    // ...

    

    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 int hashCode() {

        return firstName.hashCode() & lastName.hashCode() & age;

    }

 

    // ...

 

    private String firstName;

    private String lastName;

    private int age;

    private List<Person> children = new ArrayList<Person>();

}

 

 

// MissingHash.java

import java.util.*;

 

public class MissingHash

{

    public static void main(String[] args)

    {

        Person p1 = new Person("Ted", "Neward", 39);

        Person p2 = new Person("Charlotte", "Neward", 38);

        System.out.println(p1.hashCode());

       

        Map<Person, Person> map = new HashMap<Person, Person>();

        map.put(p1, p2);

       

        p1.setLastName("Finkelstein");

        System.out.println(p1.hashCode());

       

        System.out.println(map.get(p1));

    }

}

 

很显然,这种方法很糟糕,但是解决方法也很简单:永远不要将可变对象类型用作 HashMap 中的键。


5. equals() Comparable

在浏览 Javadoc 时,Java 开发人员常常会遇到 SortedSet 类型(它在 JDK 中唯一的实现是 TreeSet )。因为 SortedSet java.util 包中唯一提供某种排序行为的 Collection ,所以开发人员通常直接使用它而不会仔细地研究它。清单 4 展示了:


清单 4. SortedSet ,我很高兴找到了它!

                              

import java.util.*;

 

public class UsingSortedSet

{

    public static void main(String[] args)

    {

        List<Person> persons = Arrays.asList(

            new Person("Ted", "Neward", 39),

             new Person("Ron", "Reynolds", 39),

            new Person("Charlotte", "Neward", 38),

            new Person("Matthew", "McCullough", 18)

        );

        SortedSet ss = new TreeSet(new Comparator<Person>() {

            public int compare(Person lhs, Person rhs) {

                return lhs.getLastName().compareTo(rhs.getLastName());

            }

        });

        ss.addAll(perons);

        System.out.println(ss);

    }

}

 

使用上述代码一段时间后,可能会发现这个 Set 的核心特性之一:它不允许重复。该特性在 Set Javadoc 中进行了介绍。Set 是不包含重复元素的集合。更准确地说,set 不包含成对的 e1 e2 元素,因此如果 e1.equals(e2) ,那么最多包含一个 null 元素。

但实际上似乎并非如此 尽管 单 4 中没有相等的 Person 对象(根据 Person equals() 实现),但在输出时只有三个对象出现在 TreeSet 中。

set 的有状态本质相反,TreeSet 要求对象直接实现 Comparable 或者在构造时传入 Comparator ,它不使用 equals() 比较对象;它使用 Comparator/Comparable compare compareTo 方法。

因此存储在 Set 中的对象有两种方式确定相等性:大家常用的 equals() 方法和 Comparable/Comparator 方法,采用哪种方法取决于上下文。

更糟的是,简单的声明两者相等还不够,因为以排序为目的的比较不同于以相等性为目的的比较:可以想象一下按姓排序时两个 Person 相等,但是其内容却并不相同。

一定要明白 equals() Comparable.compareTo() 两者之间的不同 实现 Set 时会返回 0 。甚至在文档中也要明确两者的区别。


结束语

Java Collections 库中有很多有用之物,如果您能加以利用,它们可以让您的工作更轻松、更高效。但是发掘这些有用之物可能有点复杂,比如只要您不将可变对象类型作为键,您就 可以用自己的方式使用 HashMap

至此我们挖掘了 Collections 的一些有用特性,但我们还没有挖到金矿:Concurrent Collections ,它在 Java 5 中引入。本 系列 的后 5 个窍门将关注 java.util.concurrent

 

分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    关于 Java Collections API 您不知道的 5 件事

    ### 关于 Java Collections API 您不知道的 5 件事 #### 1. Collections 比数组更好 在 Java 的早期阶段,为了回应 C++ 开发者对于性能的批评,Java 引入了数组这一概念。然而,随着时间的发展,Java 的 ...

    关于 Java Collections API 您不知道的 5 件事,第 2 部分

    Java Collections API 是Java编程语言中不可或缺的一部分,它提供了丰富的接口和类来操作各种集合,如List、Set、Map等。这篇博客的第二部分将深入探讨关于Collections API的一些不那么为人所知的知识点,旨在帮助...

    关于 Java Collections API 您不知道的 5 件事,第 1 部分

    ### 关于 Java Collections API 您不知道的 5 件事,第 1 部分 在 Java 开发领域,Collections API 已成为一种不可或缺的工具,它不仅简化了许多常见的编程任务,还提供了丰富的特性和灵活性。然而,对于许多开发者...

    Apress的《Java Collections》

    《Java Collections》是Apress出版社出版的一本专著,它深入浅出地讲解了Java Collections框架,这是一套用于管理对象集合的强大的API。这本书针对Java程序员,无论你是初学者还是经验丰富的开发者,都能从中...

    Java Collections.pdf

    根据提供的文件信息,我们可以推断出这是一本关于Java Collections的书籍,作者为John Zukowski。下面将基于这些信息来生成相关的Java Collections知识点。 ### Java Collections 概览 #### 一、简介 Java ...

    Java API_5.0中文版

    Java API是Java编程语言的核心组成部分,它包含了各种预定义的类和接口,为开发者提供了丰富的功能,使得开发者能够构建复杂的软件系统。Java API_5.0中文版是Sun Microsystems(后被Oracle收购)官方发布的文档,是...

    java api1.6、1.7

    Java API 1.6 和 1.7 是Java开发的关键组成部分,它们包含了Java标准库的详细文档,这些库是编写Java应用程序和服务器端程序的基础。这两个版本在Java发展历程中占据了重要地位,引入了许多新特性,优化了性能,并...

    java collections

    10. **Stream API**: 自Java 8引入的Stream API为处理集合提供了新的方式,支持函数式编程风格,如过滤、映射、归约等操作。 了解和熟练使用Java集合框架是成为一名合格Java开发者的必要条件。通过深入学习这些知识...

    java 常用api大全

    这些API文档是开发者日常编程中不可或缺的参考资料,它们提供了详细的类库说明、方法解释以及示例代码,帮助开发者理解和使用Java平台的各种功能。 首先,JDBC(Java Database Connectivity)是Java访问数据库的...

    java1.6API文档可搜索

    Java 1.6 API 文档是 Java 开发者的重要参考资料,它包含了 Java 1.6 版本的所有公共类、接口、方法和异常等详细信息。这个可搜索的版本更便于开发者快速查找和理解相关功能,提高编程效率。在本文中,我们将深入...

    JAVA 1.8 API官方文档中文版

    Java 1.8 API官方文档是Java开发者不可或缺的参考资料,它详尽地介绍了Java开发平台的核心类库,涵盖了从基本数据类型、对象创建到高级特性如多线程、网络编程、I/O流、反射、泛型等各个方面。这份文档的中文版使得...

    JAVA API 6.0中文文档

    Java API 6.0中文文档是Java开发者的重要参考资料,它详尽地介绍了Java SE(标准版)6.0中的各种类库、接口和方法。...因此,无论是初学者还是经验丰富的开发者,Java API 6.0中文文档都是不可或缺的工具。

    Java jdk api 1.8_google.CHM JDK API 1.8谷歌翻译中文版在线参考手册

    《Java JDK API 1.8谷歌翻译中文版在线参考手册》是Java开发者不可或缺的重要参考资料,它详尽地列出了Java开发工具包(JDK)1.8版本中的各种类库、接口、方法和异常,为程序员提供了清晰的API文档。这份手册通过...

    java6.0 api 中文文档chm

    Java 6.0 API中文文档是Java开发人员的重要参考资料,它包含了Java标准版6.0的所有公共类、接口和方法的详细说明。这份文档以CHM(Microsoft Compiled HTML Help)格式提供,便于在Windows XP及更高版本的操作系统上...

    java api大集合

    Java API(Application Programming Interface)是Java编程语言的核心组成部分,它为开发者提供了丰富的类库和接口,使得开发人员能够高效地构建各种类型的应用程序。在Java世界中,API涵盖了从基本的输入/输出到...

    JAVA_API_1.7中文版

    8. **集合工厂方法**:Java 1.7对集合框架进行了优化,添加了工厂方法,如`Collections.emptyList()`, `Collections.singleton()`, `Map.of()`, 等,方便创建不可变的集合实例。 9. **元注解的可重复性**:从Java ...

    java通用API工具合集,最全中文版

    Java通用API工具合集是Java开发者的重要参考资料,它包含了Java平台标准版(Java SE)的各种类库和接口的详尽文档。这个最全中文版使得中国开发者能够更方便地理解和使用Java API,避免了语言障碍,提高了开发效率。...

    eclipse-collections-7.1.2-API文档-中文版.zip

    标签:eclipse、collections、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心...

Global site tag (gtag.js) - Google Analytics