Writer:BYSocket(泥沙砖瓦浆木匠)

微博:BYSocket

豆瓣:BYSocket

上一篇总结了下ArrayList 、LinkedList和Vector比较,今天泥瓦匠总结下Hash 、LinkedList和Vector比较。其实大家都是Collection,只不过有点各自特性。那就是数据结构的不同表现。

一、Set回顾

一个不包括重复元素(包括可变对象)的Collection,是一种无序的集合。Set不包含满 a.equals(b) 的元素对a和b,并且最多有一个null。
泥瓦匠的记忆宫殿:
1、不允许包含相同元素

2、判断对象是否相同,根据equals方法

二、HashSet

一个按着Hash算法来存储集合中的元素,其元素值可以是NULL。它不能保证元素的排列顺序。同样,HashSet是不同步的,如果需要多线程访问它的话,可以用 Collections.synchronizedSet 方法来包装它:

1
Set s = Collections.synchronizedSet(new HashSet(...));

同上一节一样,用迭代器的时候,也要注意 并发修改异常ConcurrentModificationException

要注意的地方是,HashSet集合判断两个元素相等不单单是equals方法,并且必须hashCode()方法返回值也要相等。看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.util.HashSet;
 
class EuqalsObj
{
    public boolean equals(Object obj)
    {
        return true;
    }
}
 
class HashCodeObj
{
    public int hashCode()
    {
        return 1;
    }
}
 
class HashSetObj
{
    public int hashCode()
    {
        return 2;
    }
 
    public boolean equals(Object obj)
    {
        return true;
    }
}
 
public class HashSetTest
{
    public static void main(String[] args)
    {
        HashSet objs = new HashSet();
        objs.add(new EuqalsObj());
        objs.add(new EuqalsObj());
        objs.add(new HashCodeObj());
        objs.add(new HashCodeObj());
        objs.add(new HashSetObj());
        objs.add(new HashSetObj());
         
        System.out.println("HashSet Elements:");
        System.out.print("\t" + objs + "\n");
    }
}

Run 一下,控制台如下输出:

1
2
HashSet Elements:
    [HashCodeObj@1, HashCodeObj@1, HashSetObj@2, EuqalsObj@1471cb25, EuqalsObj@3acff49f]

泥瓦匠根据结果,一一到来。首先,排列顺序不定。

HashSetObj 类满足我们刚刚的要求,所以集合中只有一个且它的HashCode值为2。

HashCodeObj 类虽然它们HashCode值为1,但是他们不相等。(其实当HashCode值一样,这个存储位置会采用链式结构保存两个HashCodeObj对象。)

同样,EqualsObj 类他们相等,但是他们HashCode值不等,分别为1471cb25、3acff49f。

因此,用HashSet添加可变对象,要注意当对象有可能修改后和其他对象矛盾,这样我们无法从HashSet找到准确我们需要的对象。

三、LinkedHashList

HashSet的子类,也同样有HashCode值来决定元素位置。但是它使用链表维护元素的次序。记住两个字:有序

有序的妙用,复制。比如泥瓦匠实现一个HashSet无序添加,然后复制一个一样次序的HashSet来。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.sedion.bysocket.collection;
 
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
 
public class LinkedHashListTest
{
    public static void main(String[] args)
    {
        /* 复制HashSet */
        Set h1 = new HashSet<String>();
        h1.add("List");
        h1.add("Queue");
        h1.add("Set");
        h1.add("Map");
         
        System.out.println("HashSet Elements:");
        System.out.print("\t" + h1 + "\n");
         
        Set h2 = copy(h1);
        System.out.println("HashSet Elements After Copy:");
        System.out.print("\t" + h2 + "\n");
    }
     
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Set copy(Set set)
    {
        Set setCopy = new LinkedHashSet(set);
        return setCopy;
    }
     
}

Run 一下,控制台输出:

1
2
3
4
HashSet Elements:
    [Map, Queue, Set, List]
HashSet Elements After Copy:
    [Map, Queue, Set, List]

可见,每个数据结构都有它存在的理由。

四、TreeSet

TreeSet使用树结构实现(红黑树),集合中的元素进行排序,但是添加、删除和包含的算法复杂度为O(log(n))。

举个例子吧,首先我们定义一个Bird类。(鸟是泥瓦匠最喜欢的动物)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Bird
{
    int size;
     
    public Bird(int s)
    {
        size = s;
    }
     
    public String toString()
    {
        return size + "";
    }
 
}

然后用TreeSet添加Bird类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TreeSetTest
{
    public static void main(String[] args)
    {
        TreeSet<Bird> bSet = new TreeSet<Bird>();
        bSet.add(new Bird(1));
        bSet.add(new Bird(3));
        bSet.add(new Bird(2));
         
        Iterator<Bird> iter = bSet.iterator();
         
        while (iter.hasNext())
        {
            Bird bird = (Bird) iter.next();
            System.out.println(bird);
        }
    }
}

Run一下,控制台输出如下:

1
2
3
4
5
Exception in thread "main" java.lang.ClassCastException: Bird cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(Unknown Source)
    at java.util.TreeMap.put(Unknown Source)
    at java.util.TreeSet.add(Unknown Source)
    at com.sedion.bysocket.collection.TreeSetTest.main(TreeSetTest.java:29)

答案很明显,TreeSet是排序的。所以Bird需要实现Comparable此接口。

java.lang.Comparable此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法

修改Bird如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Bird implements Comparable<Bird>
{
    int size;
     
    public Bird(int s)
    {
        size = s;
    }
     
    public String toString()
    {
        return size + "号鸟";
    }
 
    @Override
    public int compareTo(Bird o)
    {
        return size - o.size;
    }
     
}

再次Run一下:

1
2
3
1号鸟
2号鸟
3号鸟

五、性能测试比较

针对上面三种Set集合,我们对它们的Add方法进行性能测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.TreeSet;
 
class Bird implements Comparable<Bird>
{
    int size;
     
    public Bird(int s)
    {
        size = s;
    }
     
    public String toString()
    {
        return size + "号鸟";
    }
 
    @Override
    public int compareTo(Bird o)
    {
        return size - o.size;
    }
     
}
public class Set
{
    public static void main(String[] args)
    {
        Random r = new Random();
          
        HashSet<Bird> hashSet = new HashSet<Bird>();
        TreeSet<Bird> treeSet = new TreeSet<Bird>();
        LinkedHashSet<Bird> linkedSet = new LinkedHashSet<Bird>();
      
        // start time
        long startTime = System.nanoTime();
      
        for (int i = 0; i < 1000; i++) {
            int x = r.nextInt(1000 - 10) + 10;
            hashSet.add(new Bird(x));
        }
        // end time
        long endTime = System.nanoTime();
        long duration = endTime - startTime;
        System.out.println("HashSet: " + duration);
      
        // start time
        startTime = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            int x = r.nextInt(1000 - 10) + 10;
            treeSet.add(new Bird(x));
        }
        // end time
        endTime = System.nanoTime();
        duration = endTime - startTime;
        System.out.println("TreeSet: " + duration);
      
        // start time
        startTime = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            int x = r.nextInt(1000 - 10) + 10;
            linkedSet.add(new Bird(x));
        }
        // end time
        endTime = System.nanoTime();
        duration = endTime - startTime;
        System.out.println("LinkedHashSet: " + duration);
    }
}

Run一下,可以在控制台中看出:

1
2
3
HashSet: 2610998
TreeSet: 3195378
LinkedHashSet: 2673782

可见,TreeSet因为需要进行比较,所以性能比较差。

六、总结

HashSet:equlas hashcode

LinkedHashSet:链式结构

TreeSet:比较,Comparable接口,性能较差

 
 

Java 容器 & 泛型:三、HashSet,TreeSet 和 LinkedHashSet比较的更多相关文章

  1. Java 容器 & 泛型:五、HashMap 和 TreeMap的自白

    Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket Java 容器的文章这次应该是最后一篇了:Java 容器 系列. 今天泥瓦匠聊下 Maps. 一.Ma ...

  2. Java容器深入浅出之HashSet、TreeSet和EnumSet

    Java集合中的Set接口,定义的是一类无顺序的.不可重复的对象集合.如果尝试添加相同的元素,add()方法会返回false,同时添加失败.Set接口包括3个主要的实现类:HashSet.TreeSe ...

  3. Java 容器 & 泛型:一、认识容器

    Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 容器是Java语言学习中重要的一部分.泥瓦匠我的感觉是刚开始挺难学的,但等你熟悉它,接触多了,也就“顺 ...

  4. HashSet,TreeSet和LinkedHashSet的区别

    Set接口Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false.Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说,只要两个对象用eq ...

  5. Set下面HashSet,TreeSet和LinkedHashSet的区别

    Set接口Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false.Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说,只要两个对象用eq ...

  6. HashSet,TreeSet和LinkedHashSet

    Set接口 Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false. Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说,只要两个对象用 ...

  7. Java 容器 & 泛型:六、容器讲到为什么要使用泛型

    Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket ArrayList是集合类中无处不在的,泛型也是,泛型对集合类尤其有用.但是为啥要使用泛型?理解好了这 ...

  8. Java 容器 & 泛型:四、Colletions.sort 和 Arrays.sort 的算法

    Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 本来准备讲 Map集合 ,还是喜欢学到哪里总结吧.最近面试期准备准备,我是一员,成功被阿里在线笔试秒杀 ...

  9. Java 容器 & 泛型:二、ArrayList 、LinkedList和Vector比较

    Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 继续上一篇的容器文章认识容器,泥瓦匠慢慢带你们走进List的容器解说.今天泥瓦匠想说说 ArrayLi ...

随机推荐

  1. [C#.Net]Window服务调用外部程序

    最近遇到要做回传服务内增加开关,可以自定义运行一些脚本已方便收集PC状态,发现Bat始终无法运行,上网找了半天才发现和Session0有关,也就是程序有不同级别的访问权限,Vista以上版本为了安全因 ...

  2. ArrayList 和LinkedList的区别?

    ArrayList底层使用时数组.LinkedList使用的是链表. ArrayList:  数组查询具有所有查询特定元素比较快.而插入和删除和修改比较慢(数组在内存中是一块连续的内存,如果插入或删除 ...

  3. Oracle获取异常的具体出处dbms_utility.format_error_backtrace

    DBMS_UTILITY.FORMAT_ERROR_BACKTRACE :返回当前异常相应的描述,通过它就能知道异常的最初生成处.   系统为最近一次生成的异常设置了一个栈,并跟踪它的传递过程,而这个 ...

  4. (25)Teach girls bravery, not perfection

    https://www.ted.com/talks/reshma_saujani_teach_girls_bravery_not_perfection/transcript00:12So a few ...

  5. centos6安装oracle11g过程中报错Error in invoking target “install” of makefile ‘/home/oracle/app/oracle/product/11.2.0/dbhome_1/ctx/lib/ins_ctx.mk’

    报这个错误的原因是由于缺少compat_libstdc包所导致.安装即可 1.在http://www.rpm-find.net/linux/rpm2html/search.php?query=comp ...

  6. MySQL系统变量 sql_mode 详解

    转载自:http://tech.it168.com/a2012/0822/1388/000001388401_all.shtml MySQL数据类型:SQL_MODE设置不容忽视 SQL_MODE可能 ...

  7. s6 传输层

    标题 s6-1 传输层概述(2019-04-06 21:19) s6-2 UDP(2019-04-07 08:39) s6-3 通信模型(2019-04-07 08:47) s6-4 TCP 数据段( ...

  8. python迭代器与生成器详解

    迭代器与生成器 迭代器(iterator)与生成器(generator)是 Python 中比较常用又很容易混淆的两个概念,今天就把它们梳理一遍,并举一些常用的例子. for 语句与可迭代对象(ite ...

  9. mysql的ACID的理解

    这是在网上copy下来的ACID的概念,可以直接跳过看后面: 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开 ...

  10. PYTHON进阶(3)

    学习内容: 1.Python模块redis 2.Python模块memcach 3.Python模块SQLAlchemy 一.Python模块redis redis介绍 二.Python模块memca ...