资讯专栏INFORMATION COLUMN

【java源码一带一路系列】之HashSet、LinkedHashSet、TreeSet

UCloud / 1374人阅读

摘要:同样在源码的与分别见看到老朋友和。这样做可以降低性能消耗的同时,还可以减少序列化字节流的大小,从而减少网络开销框架中。使用了反射来寻找是否声明了这两个方法。十进制和,通过返回值能反应当前状态。

Map篇暂告段落,却并非离我们而去。这不在本篇中你就能经常见到她。HashSet、LinkedHashSet、TreeSet各自基于对应Map实现,各自源码内容较少,因此归纳为一篇。

HashSet
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

HashSet基于HashMap实现;而Map是键值对形式的,因此构造一个PRESENT假装为值。

同样在HashSet源码的Line273Line294分别见看到老朋友writeObject()和readObject()。使用它们自定义序列化规则,将不会调用默认的序列化方法。

这样做可以降低性能消耗的同时,还可以减少序列化字节流的大小,从而减少网络开销(RPC框架中)。[①]

记得在之前的文章中留了一个问题。即该private方法供谁调用?解释如下,然而笔者并未在ObjectOutputStream源码中找到getPrivateMethod方法,不知是否由于版本不同还是作者笔误。倒是在ObjectStreamClass中找到了getPrivateMethod()。

ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod,所以这些方法不得不被声明为priate以至于供ObjectOutputStream来使用。 [②]

/**
 * Creates a late-binding
 * and fail-fast {@link Spliterator} over the elements in this
 * set.
 *
 * 

The {@code Spliterator} reports {@link Spliterator#SIZED} and * {@link Spliterator#DISTINCT}. Overriding implementations should document * the reporting of additional characteristic values. * * @return a {@code Spliterator} over the elements in this set * @since 1.8 */ public Spliterator spliterator() { return new HashMap.KeySpliterator(map, 0, -1, 0, 0); } // HashMap源码中 static final class KeySpliterator extends HashMapSpliterator implements Spliterator { KeySpliterator(HashMap m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public KeySpliterator trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid || current != null) ? null : new KeySpliterator<>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer action) { int i, hi, mc; if (action == null) throw new NullPointerException(); HashMap m = map; Node[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = (tab == null) ? 0 : tab.length; } else mc = expectedModCount; if (tab != null && tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { Node p = current; current = null; do { if (p == null) p = tab[i++]; else { action.accept(p.key); p = p.next; } } while (p != null || i < hi); if (m.modCount != mc) throw new ConcurrentModificationException(); } } public boolean tryAdvance(Consumer action) { int hi; if (action == null) throw new NullPointerException(); Node[] tab = map.table; if (tab != null && tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { K k = current.key; current = current.next; action.accept(k); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } return false; } public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT; } }

spliterator()将Set中所有元素封装中并返回,依靠HashMap.KeySpliterator()方法来实现。HashMap.KeySpliterator重写了Spliterator接口的一些方法:

tryAdvance:如果存在没处理(action.accept(k))的数据,执行指定的代码并返回true;若不存在,直接返回false;单次;

forEachRemaining:循环对所有数据进行处理(action.accept(p.key));

trySplit:分割出一个新的Spliterator,从“mid = (lo + hi) >>> 1;”来看,KeySpliterator是对半切割的。

characteristics:返回特征值。这里只会有2种结果。Spliterator.SIZED(0x00000040)|Spliterator.DISTINCT(0x00000001)=65(十进制)和0|Spliterator.DISTINCT(0x00000001)=1,通过返回值能反应KeySpliterator当前状态。因为一旦调用以上方法处理数据,fence值就会被改变,即从65变为1(个人理解,网上资料凤毛麟角)。

“jdk1.8中的集合框架中的数据结构都默认实现了Spliterator。”(惭愧的是当时在看HashMap并没有注意到,由于Set代码行数少,反倒引起了关注。)看看下面的执行结果你是否能全部bingo呢?

HashSet hs = new HashSet();
          
hs.add("c");
hs.add("h");
hs.add("e");

Spliterator spliterator = hs.spliterator();

System.out.println("characteristics:"+spliterator.characteristics());

Spliterator spliterator2 = spliterator.trySplit();

while(spliterator.tryAdvance(t -> System.out.println("tryAdvance:"+t.toString())));

while(spliterator2.tryAdvance(t -> System.out.println("trySplit:"+t.toString())));

System.out.println("characteristics:"+spliterator.characteristics());

hs.spliterator().forEachRemaining(t -> System.out.println("forEachRemaining:"+t.toString()));
        
LinkedHashSet
/**
* Constructs a new, empty linked hash set.  (This package private
 * constructor is only used by LinkedHashSet.) The backing
 * HashMap instance is a LinkedHashMap with the specified initial
 * capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @param      dummy             ignored (distinguishes this
 *             constructor from other int, float constructor.)
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

增加dummy标志与HashSet(int initialCapacity, float loadFactor, boolean dummy)构造方法区分开来,供LinkedHashSet调用。

TreeSet

略。(是不是有种翻阅课后答案参考书的感觉- -)

说点什么:

HashSet无序;允许值为null;非线程安全;底层增删等操作基于HashMap实现;

LinkedHashSet有序;允许值为null;非线程安全;依赖于HashSet,底层增删等操作基于LinkedHashMap实现;

TreeSet有序;不允许为null;非线程安全;底层增删等操作基于TreeMap实现。

从查阅Spliterator相关资料的感受就是J8的一些技术点在国内应用貌似还不是那么普及。③中举了25个java.util.Spliterators在实际项目中的应用,感兴趣的同学可以深入学习。

更多有意思的内容,欢迎访问笔者小站: rebey.cn

知识点:

① java序列化用法以及理论(三);

② 什么是writeObject 和readObject?可定制的序列化过程【译】;

③ Java Code Examples for java.util.Spliterators;

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71136.html

相关文章

  • 3分钟搞掂Set集合

    摘要:下面总结一下集合常用的三个子类吧无序,允许为,底层是散列表红黑树,非线程同步有序,不允许为,底层是红黑树非线程同步迭代有序,允许为,底层是双向链表,非线程同步从结论而言我们就可以根据自己的实际情况来使用了。 前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码...

    widuu 评论0 收藏0
  • Java编程基础18——集合(Set集合)

    摘要:并把最终的随机数输出到控制台。方法,在集合中如何存储元素取决于方法的返回值返回,集合中只有一个元素。创建集合对象,传入比较器。 1_HashSet存储字符串并遍历 A:Set集合概述及特点 通过API查看即可 B:案例演示 HashSet存储字符串并遍历 import java.util.HashSet; public class Demo1_HashSet { p...

    SexySix 评论0 收藏0
  • java集合-Set

    摘要:集合判断两个元素的标准是两个对象通过方法比较相等,并且两个对象的方法返回值也相等。的集合元素也是有序的,以枚举值在类内的定义顺序来决定集合元素的顺序。是所有实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。 Set集合通常不能记住元素的添加顺序。Set不允许包含重复的元素。 Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作...

    xavier 评论0 收藏0
  • Java 集合 Set

    摘要:当复制集合中的所有元素来创建新的集合时,要求集合中的所有元素必须是同一个枚举类的枚举值各实现类的性能分析的性能总比好,特别是最常用的添加查询元素等操作。因为需要额外的红黑树算法来维护集合元素的次序。在创建时进行,以防对集合的意外非同步访问 HashSet 大多时候使用Set集合时就是使用HashSet实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能 ...

    verano 评论0 收藏0
  • java集合类

    摘要:集合类简介集合类包含在包下集合类存放的是对象的引用,而非对象本身。集合类型主要分为集,列表,映射。返回此有序集合中当前第一个最小的元素。集合中元素被访问的顺序取决于集合的类型。 Java集合类 1.简介: java集合类包含在java.util包下集合类存放的是对象的引用,而非对象本身。集合类型主要分为Set(集),List(列表),Map(映射)。 1.1 java集合类图 sho...

    Pluser 评论0 收藏0

发表评论

0条评论

UCloud

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<