FastUtil 是一个开源的 Java 库,它提供了一系列高性能的集合类型实现,旨在替代 Java 标准集合框架(java.util)中的某些部分,详细内容见github地址:github.com/vigna/fastu… , 以下是 FastUtil 的介绍和它的主要优势:
介绍
FastUtil 由意大利米兰大学的 Sebastiano Vigna 开发和维护,主要特点包括:
- 类型特定的集合:FastUtil 提供了针对原始数据类型(如
int,long,double等)的集合实现,这些实现避免了 Java 自动装箱和拆箱的开销。 - 紧凑的数据结构:FastUtil 的数据结构设计得非常紧凑,减少了内存占用。
- 高性能:除了内存效率外,FastUtil 还提供了快速的访问和操作性能
主要优势
- 内存效率:
- 减少自动装箱:通过使用原始数据类型,FastUtil 减少了自动装箱和拆箱所需的内存开销。
- 紧凑的数据结构:内部数据结构设计得更加紧凑,减少了内存占用。
- 性能:
- 快速访问:FastUtil 的集合通常提供了比 Java 标准集合更快的访问速度。
- 高效操作:集合操作(如添加、删除、查找)通常在 FastUtil 中更快。
- 丰富的功能:
- 多种集合类型:除了基本的集合类型(如列表、集合、映射),FastUtil 还提供了堆、优先队列等。
- 扩展的 API:FastUtil 提供了许多扩展的方法和功能,使得操作集合更加方便。
- 兼容性:
- 与 Java 标准集合框架兼容:FastUtil 的集合实现了 Java 标准集合框架中的接口,因此可以与现有的 Java 代码无缝集成。
- 易于使用
- 直观的 API:FastUtil 的 API 设计直观,易于理解和使用。
- 文档和示例:FastUtil 提供了详细的文档和示例代码,帮助开发者快速上手。
- 社区支持:
- 开源社区:作为一个开源项目,FastUtil 拥有一个活跃的社区,不断进行改进和更新
劣势
- 树结构(Trees) :FastUtil 并没有提供类似于
TreeSet或TreeMap的红黑树实现。如果你需要这样的数据结构,你可能需要使用 Java 标准库中的TreeSet或TreeMap。 - 图(Graphs) :FastUtil 没有提供图数据结构的实现。处理图结构通常需要专门的库,如 JGraphT。
- 并发数据结构(Concurrent Data Structures) :虽然 FastUtil 提供了一些线程安全的数据结构,但它没有提供与 Java 并发工具包(java.util.concurrent)中相同范围和深度的并发数据结构。
- 流(Streams) :Java 8 引入的流 API(java.util.stream)在 FastUtil 中没有对应的优化实现。
- 原子变量(Atomic Variables) :Java 提供了
java.util.concurrent.atomic包来处理原子变量,而 FastUtil 并没有提供类似的功能。 - 布隆过滤器(Bloom Filters) :这是一种空间效率极高的概率数据结构,用于测试一个元素是否属于集合。FastUtil 没有提供布隆过滤器的实现。
- 跳表(Skip Lists) :跳表是一种可以替代平衡树的数据结构,用于有序集合的快速搜索。FastUtil 没有提供跳表的实现。
- 位图(Bit Maps) :虽然 FastUtil 提供了
BitSet的优化版本LongBigArrayBitSet,但它没有提供其他类型的位图数据结构,如 Roaring Bitmaps。 - Trie(前缀树) :用于高效地存储和检索字符串集合的数据结构,FastUtil 没有提供这种数据结构的实现。
- 索引结构(Index Structures) :例如 R-tree 或 B-tree,这些是用于空间数据索引的数据结构,FastUtil 并没有提供
适用场景
- 处理大量数据:当需要处理大量数据时,FastUtil 的内存效率和性能优势尤为明显。
- 性能敏感的应用:对于性能要求较高的应用,FastUtil 可以提供更好的性能。
- 需要紧凑数据结构:当内存资源有限时,FastUtil 的紧凑数据结构可以节省内存
项目引入依赖
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
<version>8.5.15</version>
</dependency>
<!--测试对象在内存中占用的字节数-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>8.7.0</version>
</dependency>
功能介绍
1.内存开销对比
Map<Integer, Integer> map=new HashMap<>(1<<8);
for (int i = 0; i < 100000; i++) {
map.put(i, i+100000);
}
System.out.println("Jdk中map--内存字节数:"+RamUsageEstimator.sizeOfObject(map));
Int2IntArrayMap fastUtilMap=new Int2IntArrayMap(map);
System.out.println("fastUtilMap--内存字节数:"+RamUsageEstimator.sizeOfObject(fastUtilMap));
结果:
2. 性能对比
案例1
在这个例子中,我们将比较 java.util.ArrayList 和 it.unimi.dsi.fastutil.ints.IntArrayList 在添加元素时的性能
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(1)
public class CollectionBenchmark {
@Param({"10000", "100000", "1000000"})
private int size;
private int[] values;
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(CollectionBenchmark.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
@Benchmark
public void testJavaArrayList(Blackhole bh) {
ArrayList<Integer> list = new ArrayList<>();
for (int value : values) {
list.add(value);
}
bh.consume(list);
}
@Benchmark
public void testFastUtilIntArrayList(Blackhole bh) {
IntArrayList list = new IntArrayList();
for (int value : values) {
list.add(value);
}
bh.consume(list);
}
@Setup
public void setup() {
values = new int[size];
for (int i = 0; i < size; i++) {
values[i] = i;
}
}
}
测试结果,可以看出操作数据量较大时候优势挺大,比JDk性能强
案例2
为了全面比较 FastUtil 和 JDK 标准集合在 Map 操作上的性能,我们可以创建一个综合操作的基准测试案例,其中包括插入、检索、更新和删除操作。以下是一个使用 JMH (Java Microbenchmark Harness) 的测试案例
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(1)
public class MapOperationsBenchmark {
@Param({"10000", "100000", "1000000"})
private int size;
private int[] keys;
private Map<Integer, Integer> javaMap;
private Int2IntOpenHashMap fastUtilMap;
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(MapOperationsBenchmark.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
@Benchmark
public void testJavaMapOperations(Blackhole bh) {
for (int key : keys) {
javaMap.put(key, key);
bh.consume(javaMap.get(key));
javaMap.put(key, key + 1);
bh.consume(javaMap.remove(key));
}
}
@Benchmark
public void testFastUtilMapOperations(Blackhole bh) {
for (int key : keys) {
fastUtilMap.put(key, key);
bh.consume(fastUtilMap.get(key));
fastUtilMap.put(key, key + 1);
bh.consume(fastUtilMap.remove(key));
}
}
@Setup
public void setup() {
keys = new int[size];
for (int i = 0; i < size; i++) {
keys[i] = ThreadLocalRandom.current().nextInt(size);
}
javaMap = new HashMap<>();
fastUtilMap = new Int2IntOpenHashMap();
}
}
结果如下,在操作基本类型时候,fastUtil优势还是非常之大
3. 集合运用
- 原始类型集合:例如
IntList,LongSet,Double2IntMap等,这些集合直接使用原始数据类型(如int,long,double)作为元素,避免了 Java 自动装箱和拆箱的开销。
IntList基本类型简单运用案例
//创建IntArrayList或者使用静态方法IntArrayList.wrap(new int[] { 42, 24 })
final IntArrayList list = new IntArrayList();
list.add(24);
//使用Iterator 迭代器
final IntListIterator it = list.listIterator();
it.add(42);
assertTrue(it.hasNext());
//也可以使用foreach遍历
list.forEach(System.out::println);
assertEquals(IntArrayList.wrap(new int[] { 42, 24 }), list);
//排序
list.sortStable();
- 对象集合:例如
ObjectList,ObjectSet,Object2ObjectMap等,这些集合可以存储任意类型的对象。注意 FastUtil 的对象集合可以存储任意类型的对象,但其性能优势主要体现在原始类型集合上。对于对象集合,性能和内存效率可能不如原始类型集合那么显著
ObjectList简单运用案例
import it.unimi.dsi.fastutil.objects.ObjectList;
public class FastUtilExample {
public static void main(String[] args) {
// 创建一个 ObjectList 来存储 Person 对象
ObjectList<Person> people = new ObjectArrayList<>();
// 添加 Person 对象到列表中
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));
// 遍历列表并打印 Person 对象的信息
for (Person person : people) {
System.out.println("Name: " + person.getName() + ", Age: " + person.getAge());
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getters and setters omitted for brevity
}
- 可变大小数组:例如
IntArrayList,LongArraySet,IntRBTreeSet,IntAVLTreeSet, 等,这些集合提供了动态调整大小的数组功能。
简单运用案例
IntRBTreeSet 进行多种操作,包括添加、删除、查找和遍历操作,
IntRBTreeSet 是基于红黑树实现的,它保证了以下性质:
- 树的高度相对较小,因此查找、插入和删除操作的平均时间复杂度为 O(log n)。
- 树中的元素保持有序
IntRBTreeSet<Integer> intRBTreeSet = new IntRBTreeSet<>();
// 添加元素
intRBTreeSet.add(1);
intRBTreeSet.add(3);
intRBTreeSet.add(5);
intRBTreeSet.add(7);
// 删除元素
intRBTreeSet.remove(3);
// 查找元素
boolean contains3 = intRBTreeSet.contains(3); // 返回 false,因为 3 已被删除
boolean contains5 = intRBTreeSet.contains(5); // 返回 true
// 遍历集合
for (int element : intRBTreeSet) {
System.out.println(element);
}
- 映射:例如
Int2IntMap,Object2IntMap,Int2IntMapGenericAVL,Int2IntMapGenericRB等,这些映射将原始类型或对象映射到原始类型或对象。
简单运用案例
Int2IntMapGenericAVL 是 FastUtil 库中提供的一个基于 AVL 树的整数到整数的映射实现。AVL 树是一种自平衡的二叉搜索树,它确保了树的高度相对较小,从而提供了良好的性能。
Int2IntMapGenericAVL 是基于 AVL 树实现的,它保证了以下性质:
- 树的高度相对较小,因此查找、插入和删除操作的平均时间复杂度为 O(log n)。
- 树中的键保持有序。
Int2IntMapGenericAVL<Integer> int2IntMap = new Int2IntMapGenericAVL<>();
// 添加元素
int2IntMap.put(1, 3);
int2IntMap.put(3, 5);
int2IntMap.put(5, 7);
int2IntMap.put(7, 9);
// 删除元素
int2IntMap.remove(3); // 移除键为 3 的元素
// 查找元素
int value = int2IntMap.get(5); // 返回键为 5 的元素的值
// 遍历映射
for (Int2IntMap.Entry<Integer> entry : int2IntMap.int2IntEntrySet()) {
System.out.println("Key: " + entry.getIntKey() + ", Value: " + entry.getIntValue());
}
BigArrays是 FastUtil 库的一部分,它提供了一种高效的方式来操作超大型数组。BigArrays专门为处理大数据集而设计,可以有效地管理数组中的元素,并且提供了多种操作方法,如排序、查找、插入和删除等。
// 创建一个 BigArrays 实例
IntBigArrays bigArrays = IntBigArrays.factory();
// 创建一个初始为空的数组
int[] array = bigArrays.makeEmpty();
// 添加元素到数组
bigArrays.add(array, 0, 1);
bigArrays.add(array, 1, 3);
bigArrays.add(array, 2, 5);
bigArrays.add(array, 3, 7);
// 打印原始数组
System.out.println("Original array:");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
// 对数组进行归并排序
bigArrays.mergeSort(array, 0, array.length);
//也可以使用 BigArrays.quickSort(0, length(array), (k1, k2) -> get(array, k1) - get(array, k2), (k1, k2) -> swap(array, k1, k2));
// 打印排序后的数组
System.out.println("Sorted array:");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}