Trove 3.0.0

                                                                                           Trove 3.0.0使用背景介绍  

Trove 是一个快速、轻量级 Collection 类的集合。Trove 提供所有标准 java.util Collections 类的更快的版本以及能够直接在原(primitive)(例如包含 int 键或值的 Map 等)上操作的 Collections 类的版本。即它是一种开放源代码的 Java 集合包,提供了核心 Java 集合类的高效替代品,特别针对于实现其键或值是基本类型的集合
Trove 中有许许多多的高效集合,除了那 81 种不同的 HashMap 版本之外(比如 TIntIntHashMap 、 TIntObjectHashMap等),还有 List 和 Set 类可以存储基本类型(如 TIntHashSet 、 TFloatArrayList 等)。这种体系结构甚至允许你插入自己的散列策略,以便选择对你的数据集而言可能更有效(或者更灵活)的算法。当然,支持灵活的散列算法也要付出性能代价 —— 如果散列映射成为瓶颈,很可能是因为大量地使用它,这意味着散列函数被反复不断地调用。因此,散列函数中每一点多余的开销都可能成为瓶颈。(顺便说一下,如果散列函数足够简单,JIT 编译器就可能将其编译成内联函数,这样在支持灵活的散列策略的同时又不会带来额外的开销)

trove是轻量级实现java.util Collections API的第三方开源项目 官网:  http://trove.starlight-systems.com/overview trove相比jdk原生的集合类有三个优势: 1、更高的性能 2、更底的内存消耗 3、除了实现原生Collections API并额外提供更强大的功能。

集合是java编程最常用的API之一,把项目的集合对象改用trove替换就能获得性能提升和内存的节省。 这是对一个项目底成本的优化方案。

第一章 Map的介绍

1.1 key-value 基础类型篇

1.1.1 接口命名规范

对于key-value 都是基础类型(int,long,double,float,byte,char,short等),都可以使用接口名为T+基础类型(key+基础类型(value+Map的接口。如 TIntIntMap

1.1.2 接口方法介绍

因为方法的英文名的意思很清楚了,就不一一介绍了。
    int getNoEntryKey();

    int getNoEntryValue();

    int put(int var1, int var2);

    int putIfAbsent(int var1, int var2);

    void putAll(Map<? extends Integer, ? extends Integer> var1);

    void putAll(TIntIntMap var1);

    int get(int var1);

    void clear();

    boolean isEmpty();

    int remove(int var1);

    int size();

    TIntSet keySet();

    int[] keys();

    int[] keys(int[] var1);

    TIntCollection valueCollection();

    int[] values();

    int[] values(int[] var1);

    boolean containsValue(int var1);

    boolean containsKey(int var1);

    TIntIntIterator iterator();

    boolean forEachKey(TIntProcedure var1);

    boolean forEachValue(TIntProcedure var1);

    boolean forEachEntry(TIntIntProcedure var1);

    void transformValues(TIntFunction var1);

    boolean retainEntries(TIntIntProcedure var1);

    boolean increment(int var1);

    boolean adjustValue(int var1, int var2);

    int adjustOrPutValue(int var1, int var2, int var3);

1.1.3 实现类介绍和规范

对于key-value 都是基础类型,都有三种不同的实现类,关键使用特征分为hashmapSynchronized,Unmodifiable。下面分别以TIntIntMap介绍下每个实现类的使用场景:

(1) Hashmap:这种是工作中常用的方法,他的优点就是hash散列表的算法实现了快速的添加和查找。使用方法,如下图所示,其中当知道容器大概大小时,尽量初始化容器大小。

int size=16;
TIntIntMap tIntIntHashMap=newTIntIntHashMap(size);
tIntIntHashMap.put(1 ,1);
tIntIntHashMap.get(1);

(2) Synchronized:这种是容器在多线程中承担共享资源的使用的实现类,它会在每次的putset前对容器进行加锁操作,其性能比JDK的原生的快。使用方法有两种,如下图所示。

  //第一种方式
        TSynchronizedIntIntMap synchronizedIntIntMap =new TSynchronizedIntIntMap(tIntIntHashMap);
        synchronizedIntIntMap.put(1 ,1);
        synchronizedIntIntMap.get(1);
        //第二种方式
        TCollections.synchronizedMap(tIntIntHashMap);

(3) Unmodifiable:这种事容器在不修改的时候使用,其实这个不可修改的Map指的是Map中的对象地址不可修改,里面的对象若支持修改的话,其实也还是可以修改的。请看下面的例子。

package util;

import gnu.trove.TCollections;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;


public class SeeminglyUnmodifiable {
    private TIntObjectMap<Point> startingLocations=new TIntObjectHashMap<Point>();


    public SeeminglyUnmodifiable(){
        startingLocations.put(1, new Point(1, 1));
        startingLocations.put(2, new Point(1, 2));
        startingLocations.put(3, new Point(1, 3));
        //..more locations..
    }

    public TIntObjectMap<Point> getStartingLocations(){
        return TCollections.unmodifiableMap(startingLocations);
    }

    public static void main(String [] args){
        SeeminglyUnmodifiable  pieceLocations = new SeeminglyUnmodifiable();
        TIntObjectMap<Point> locations = pieceLocations.getStartingLocations();

        Point camelLoc = locations.get(3);
        System.out.println("The LeftCamel's start is at [ " + camelLoc.getX() +  ", " + camelLoc.getY() + " ]");

        //Try 1. update elicits Exception
        try{
            locations.put(3, new Point(0,0));
        } catch (java.lang.UnsupportedOperationException e){
            System.out.println("Try 1 - Could not update the map!");
        }

        //Try 2. Now let's try changing the contents of the object from the unmodifiable map!
        camelLoc.setLocation(0,0);

        //Now see whether we were able to update the actual map
        Point newCamelLoc = pieceLocations.getStartingLocations().get(3);
        System.out.println("Try 2 - Map updated! The LeftCamel's start is now at [ " + newCamelLoc.getX() +  ", " + newCamelLoc.getY() + " ]");
        //Try 2. Now let's try changing the contents of the object from the unmodifiable map!
        try {

            locations.put(200, new Point(6, 7));
            //Now see whether we were able to update the actual map
            Point try3 = pieceLocations.getStartingLocations().get(4);
            System.out.println("Try 3- Map updated! The LeftCamel's start is now at [ " + try3.getX() +  ", " + try3.getY() + " ]");
        }
        catch (Exception e)
        {
            System.out.println("Try 3 dont new add");
        }
    }
}

class Point{
    public float x;
    public float y;
    public Point(float x, float y){
        setLocation(x, y);
    }
    public void setLocation(float x, float y){
        this.x = x;
        this.y = y;
    }

    public float getX(){
        return x;
    }

    public float getY(){
        return y;
    }
}

1.2 Object-value 或 key-Object 对象类型篇

1.2.1 接口命名规范

Object-value 或 key-Object的容器中,都可以使用接口名为T+Objectkey+基础类型(value+Map 或T+基础类型key+Objectvalue+Map 的接口,为了满足的Java的泛型规范,在后面需要加上你Object的泛型类。如 TIntObjectMap<Point> 或者TObjectIntMap<Point>

第二章 List 篇

2.1 接口命名规范

的容器中,都可以使用接口名为T+基础类型(value+Array或Linked+List。如 TIntArrayList或TIntLinkedList

2.2 接口方法介绍

 下面函数值得注意的是trove的List类型支持一对多的映射,即集合对象可以是一个数组。
 int getNoEntryValue();

    int size();

    boolean isEmpty();

    boolean add(int var1);

    void add(int[] var1);

    void add(int[] var1, int var2, int var3);

    void insert(int var1, int var2);

    void insert(int var1, int[] var2);

    void insert(int var1, int[] var2, int var3, int var4);

    int get(int var1);

    int set(int var1, int var2);

    void set(int var1, int[] var2);

    void set(int var1, int[] var2, int var3, int var4);

    int replace(int var1, int var2);

    void clear();

    boolean remove(int var1);

    int removeAt(int var1);

    void remove(int var1, int var2);

    void transformValues(TIntFunction var1);

    void reverse();

    void reverse(int var1, int var2);

    void shuffle(Random var1);

    TIntList subList(int var1, int var2);

    int[] toArray();

    int[] toArray(int var1, int var2);

    int[] toArray(int[] var1);

    int[] toArray(int[] var1, int var2, int var3);

    int[] toArray(int[] var1, int var2, int var3, int var4);

    boolean forEach(TIntProcedure var1);

    boolean forEachDescending(TIntProcedure var1);

    void sort();

    void sort(int var1, int var2);

    void fill(int var1);

    void fill(int var1, int var2, int var3);

    int binarySearch(int var1);

    int binarySearch(int var1, int var2, int var3);

    int indexOf(int var1);

    int indexOf(int var1, int var2);

    int lastIndexOf(int var1);

    int lastIndexOf(int var1, int var2);

    boolean contains(int var1);

    TIntList grep(TIntProcedure var1);

    TIntList inverseGrep(TIntProcedure var1);

    int max();

    int min();

    int sum();

2.3 接口的实现介绍和规范

list容器的实现分为array和linked。下面分别介绍下使用场景。

(1)array:它是以数组的方式来实现的,数组的特性是可以使用索引的方式来快速定位对象的位置,因此对于快速的随机取得对象的需求,使用ArrayList实现执行效率上会比较好. 

(2) linked:是采用链表的方式来实现List接口的,它本身有自己特定的方法,如: addFirst(),addLast(),getFirst(),removeFirst()等. 由于是采用链表实现的,因此在进行insert和remove动作时在效率上要比ArrayList要好得多!适合用来实现Stack(堆栈)与Queue(队列),前者先进后出,后者是先进先出.


Trove的背后原理

好了,上面是一段小插曲,您到底能够从 Trove 中得到什么呢?许许多多的高效集合。除了那 81 种不同的 HashMap 版本之外

(比如TIntIntHashMap 、 TIntObjectHashMap 等),还有 List 和 Set 类可以存储基本类型(如 TIntHashSet 、 TFloatArrayList 等)。这种体系结构甚至允许您插入自己的散列策略,以便选择对您的数据集而言可

能更有效(或者更灵活)的算法。当然,支持灵活的散列算法也要付出性能代价 —— 如果散列映射成为瓶颈,很可能是因为大量

地使用它,这意味着散列函数被反复不断地调用。因此,散列函数中每一点多余的开销都可能成为瓶颈。(顺便说一下,如果散列

函数足够简单,JIT 编译器就可能将其编译成内联函数,这样在支持灵活的散列策略的同时又不会带来额外的开销 —— 真是免费的午餐!)

此外,Eric 还实现了 Smalltalk 范型,就是说集合能够对自己的元素执行过程。这种处理不同于 Java 平台中一般的集合元素遍历,

需要稍微讨论一下。在 Java 编程中,如果需要处理集合中的所有元素,通常要有一个迭代器(Iterator)来访问和处理每个元素,

如清单 1 所示:

清单 1. 集合的遍历
  Iterator iterator = collection.iterator();
  while (iterator.hasNext())
  {
    Object o = iterator.next();
    ... //do something with 'o'
  }

Trove 也支持这种迭代模式,但是还支持向集合传递过程,然后集合在内部迭代每个元素,依次将该过程应用于每个元素。尽管这

种技术不一定能提高效率 —— 如果迭代器对象不能像集合本身那样高效地遍历元素,则可以提高效率 —— 但是有助于提高清晰性

和灵活性。清单 2 是上述例子改写后的版本:

清单 2. 向集合传递过程
Procedure procedure = new DoSomethingWithOProcedure();
collection.forEach(procedure);

开放选址

Trove 映射是采用开放选址而不是链接来实现的。在 Java 核心集合类中,多数映射都是使用链接实现,就是说如果多个键映射到

表中的同一索引位置,则索引位置保存一个链表,其中存放映射到该位置的所有元素。开放选址映射则假设表中邻近的位置存在

没有使用的索引。如果目标位置已经被占用,映射实现就查看附近的几个位置找到一个没有使用的位置。这种方法不需要链表节点,因此 Trove 映射和相同的核心集合类相比占用的内存更少。使用开放选址必须保证有足够的空闲索引,否则可能影响性能。

(Trove 保

持装载因子小于 0.5。)否则的话,开放选址的效率与链接基本相当,但多数情况下要好于后者。

开放选址的另一个优点是实现中不需要链表节点对象,链表节点需要靠链接来实现。为什么特别强调这一点呢?基本上每个 JVM

 版本都会改进对象创建和无用单元回收的性能,但是较多的对象总会带来更多的开销。对于任何特定的问题,Java 编程中能够减

少对象数量的解决方案通常都有更好的效率,这是一个标准的性能权衡问题,每个有经验的性能优化人员都知道。开放选址使用

更少的对象来维护映射结构,这意味着 Trove 映射在多数情况下比核心 Java 映射更有效,也更小。值得一提的是,最近越来越多的 

Java 核心 Map 实现开始使用开放选址(比如IdentityHashMap 类)。现有的其他核心 Java Map 实现最终也可能改为使用开放

选址,虽然我们可能不那么关心,因为如果需要开放选址的实现,使用 Trove 就可以了。

Trove 本身提供了一个小的基准测试,用于跟核心 Java 集合进行性能比较。Dion Almaer 所进行的独立比较测试(请参阅 参考资

)说明了 Trove 在性能和大小上的优势。结果表明,Trove 执行 Map.put() 操作的速度要快三倍,而占用的内存只有核心 Map

所需要的一半。

自动装箱

Java 5.0 平台引入了泛化(generics)和自动装箱机制(Autoboxing)。这意味着您可以编写下面这样的代码:

清单 3. 使用自动装箱机制
  mymap.put("201",201);
  mymap.put("202",202);
  int sum = mymap.get("201") + mymap.get("202")
  mymap.put("sum",sum);

注意,通过使用泛化, mymap 甚至可以限制为仅保存 Integer 对象。但是也要注意在幕后仍然会进行自动装箱,清单 3 代码中

的所有 int 都在运行时使用 Integer 对象包装(通常每次都使用新的对象),并使用存取方法来访问(虽然 JIT 编译器可能将

访问编译成内联函数)。

需要强调指出:Java 5.0 平台中的自动装箱可能造成高效的假象。在代码层上看起来基本数据类型的使用效率很高,但是在运行

时,基本数据包装器类型的使用效率则会变得很低。Heinz Kabutz 博士最近撰文表明,如果不小心在循环中使用自动装箱机制,

有可能使性能降低一个数量级(请参阅 参考资料)。

自动装箱的效率比不上使用直接保存基本数据类型的集合,比如值为 int 、键为 Object 的 Trove TObjectIntHashMap 类型

不需要包装和解包int 值。对于其他基本数据类型和集合组合,Trove 都有等价的集合,如 TIntIntHashMap ( int 键和 int 值)、 TLongArrayList (保存long 的列表)、 TIntSet (保存 int 的集合)等等。Dion Almaer 对 TIntIntHashMap 和 HashMap 的比较测试也表明 Trove 的速度要快一个数量级。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值