Java容器——Set和顺序存储

    当Set使用自己创建的类型时,存储的顺序如何维护,在不同的Set实现中会有不同,而且它们对于在特定的Set中放置的元素类型也有不同的要求:

Set(interface) 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set和Collection具有完全一样的接口,但Set不保证元素的顺序。
HashSet* 为快速查找而设计的Set。存入HashSet的元素必须定义hashCode()方法
TreeSet 一种可维护元素顺序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口。
LinkedHashSet 具有HashSet的查询速度,而且内部使用链表维护元素的顺序(插入的顺序),于是在使用迭代器遍历Set时,结果会按元素插入的顺序显示。元素也必须定义hashCode()方法

    在HashSet打*号,表示如果没有其他的限制,这就应该是默认的选择,因为它的速度很快。

    你必须为散列存储和树形存储都定义一个equals()方法,但是hashCode()只有在这个类将会被放入HashSet或者LinkedHashSet中才是必须的。但是对于良好的变成风格而言,你应该在覆盖equals()方法的同时覆盖hashCode()方法。下面的示例演示了为了成功的使用特定的Set实现类而必须定义的方法:

?


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

import java.util.HashSet;

import java.util.LinkedHashSet;

import java.util.Set;

import java.util.TreeSet;

 

class SetType {

    int i;

    public SetType (int n) { i = n; }

    public boolean equals(Object obj) {

        return obj instanceof SetType && (i == ((SetType)obj).i);

    }

    public String toString() { return Integer.toString(i); }

}

 

//能正常被HashSet,LinkedHashSet使用的类型

class HashSetType extends SetType {

    public HashSetType(int n) { super(n); }

    public int hashCode() { return i; }

}

 

//能正常被TreeSet使用的类型

class TreeSetType extends SetType implements Comparable<TreeSetType>{

    public TreeSetType(int n) { super(n); }

    public int compareTo(TreeSetType o) {

        return (o.i < i ? -1 : (o.i > i ? 1 0));//降序排列

    }

}

 

public class TypesForSets {

    static <T> Set<T> fill(Set<T> set, Class<T> clazz) {

        try {

            for (int i = 0; i < 10; i++) {

                set.add(clazz.getConstructor(int.class).newInstance(i));

            }

        catch (Exception e) {

            throw new RuntimeException(e);

        }

        return set;

    }

    static <T> void test(Set<T> set, Class<T> clazz) {

        fill(set, clazz);

        fill(set, clazz);//尝试重复向Set中添加

        fill(set, clazz);

        System.out.println(set);

    }

    public static void main(String[] args) {

        //正确的装法

        System.out.println("---------Correct----------");

        test(new HashSet<HashSetType>(), HashSetType.class);

        test(new LinkedHashSet<HashSetType>(), HashSetType.class);

        test(new TreeSet<TreeSetType>(), TreeSetType.class);

        //错误的装法

        System.out.println("---------Wrong----------");

        test(new HashSet<SetType>(), SetType.class);

        test(new HashSet<TreeSetType>(), TreeSetType.class);

        test(new LinkedHashSet<SetType>(), SetType.class);

        test(new LinkedHashSet<TreeSetType>(), TreeSetType.class);

        try {

            test(new TreeSet<SetType>(), SetType.class);

        catch (Exception e) {

            System.out.println(e.getMessage());

        }

        try {

            test(new TreeSet<SetType>(), SetType.class);

        catch (Exception e) {

            System.out.println(e.getMessage());

        }

    }

}

    执行结果(样例):

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

---------Correct----------

[0123456789]

[0123456789]

[9876543210]

---------Wrong----------

[1150734659807596

    82417436822093]

[3518541093085769

    73407621286942]

[0123456789012345

    67890123456789]

[0123456789012345

    67890123456789]

java.lang.ClassCastException: 

    SetType cannot be cast to java.lang.Comparable

java.lang.ClassCastException: 

    SetType cannot be cast to java.lang.Comparable

    为了证明哪些方法是对于某种特殊的Set是必须的,同时也为了避免代码重复,我们创建了三个类型。基类SetType只存储了一个int,并且通过toString()方法产生它的值。因为所有在Set中存储的类型都必须具有equals()方法,因此在基类中也有该方法。

    HashSetType继承自SetType,并添加了hashCode()方法,该方法对于放入Set的散列实现中的对象来说是必需的。

    TreeType实现了Comparable接口,如果一个对象被用于任何种类的排序容器中,例如TreeSet,那么它必须实现这个接口。注意:在compareTo()方法中,我没有使用简洁明了的形式return o.i-i,因为这是一个常见的编程错误,它只有在i和i2都是无符号的int(如果Java确实有unsigned关键字的话)才能正常工作。对于有符号的int,它就会出错,因为int不够大,不足以表现两个有符号的int的差。例如o.i是很大的正数而且i是很小的负数时,i-j就会溢出并返回负值,这就不对了。

    你通常希望compareTo()产生与equals()一致的自然顺序。如果equals()对于某个特定的比较返回true,那么compareTo()对于该比较就应该返回0,反之亦然。

    在TypesForSets中,fill()和test()方法都是用泛型定义的,这是为了避免代码重复。为了验证某个Set的行为,test()会在被测Set上调用三次,尝试着在其中添加重复对象。fill()方法接受任意类型的Set,以及对应类型的Class对象,它使用Class对象来发现构造器并构造对象后添加到Set中。

    从输出可以看到,HashSet以某种顺序保存所有的元素(这结果是我用 jdk1.7.0_79 跑出来的,而书中描述是用的jdk1.5,因此不知道是不是这里存在的差异。我这里使用HashSet的元素的结果是有序的,但书中顺序是乱的),LinkedHashSet按照元素插入的顺序保存元素,而TreeSet则按照排序(按照compareTo()定义的顺序,这里是降序)保存元素

    如果我们尝试着将没有恰当地支持必须操作的类型用于这些方法的Set,那么就会有麻烦了。对于没有重新定义hashCode()方法的SetType或TreeType,如果将它们放置到任何散列表中都会产生重复值,这样就违反了Set的基本约定。这相当烦人,因为这种情况甚至不会有运行时错误。

时间: 2024-08-01 12:40:31

Java容器——Set和顺序存储的相关文章

阿里中间件技术专家魏鹏:基于Java容器的多应用部署技术实践

首届阿里巴巴在线技术峰会(Alibaba Online Technology Summit),将于7月19日-21日 20:00-21:30 在线举办.本次峰会邀请到阿里集团9位技术大V,分享电商架构.安全.数据处理.数据库.多应用部署.互动技术.Docker持续交付与微服务等一线实战经验,解读最新技术在阿里集团的应用实践. 阿里巴巴在线技术峰会专题:https://yq.aliyun.com/activity/97峰会统一报名链接:https://yq.aliyun.com/webinar/j

Java容器——未获支持的操作

    先看一个例子: ? 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 import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import 

java容器 collection-JAVA中什么是容器?什么是集合?求大神指导

问题描述 JAVA中什么是容器?什么是集合?求大神指导 容器是什么?跟接口是什么关系?集合又是什么?求大神帮忙?????? 解决方案 Java集合容器主要有以下几类: 1,内置容器:数组 2,list容器:Vetor,Stack,ArrayList,LinkedList, CopyOnWriteArrayList(1.5),AttributeList(1.5),RoleList(1.5),RoleUnresolvedList(1.5), ConcurrentLinkedQueue(1.5),Ar

【阿里在线技术峰会】魏鹏:基于Java容器的多应用部署技术实践

本文首先向大家介绍阿里Java容器的发展历程,整个Java容器从开始到现在经历了哪些阶段,接着给大家分享目前Java容器的基础架构,最后与大家探讨经过这样的改变之后,它能够完成的一些高阶的特性,比如合并部署和多版本部署等对于我们的效率提升有明显的帮助的技术. 直播视频:点此进入 PDF下载:点此进入 以下为整理内容:   阿里Java容器的发展历程 阿里巴巴在开始阶段,就像普通的网站一样,通过前端的http的流量进来完成数据库的查询.调用,然后把数据反馈回去.当网站变得很大的时候,不能再像过去一

Java 容器 &amp; 泛型:五、HashMap 和 TreeMap的自白

一.Map回顾     Map,又称映射表,是将键映射到值的对象.有四种实现Map接口并且经常使用的Map集合为:HashMap,TreeMap,Hashtable 和 LinkedHashMap. 泥瓦匠记忆宫殿:     1.一个映射不包含重复的键.     2.每个键最多只能映射到一个值. 二.HashMap     HashMap是基于哈希表的Map接口的实现.其对键进行散列,散列函数只能作用于键.下面模拟下,公司员工和找员工的例子: ? 1 2 3 4 5 6 7 8 9 10 11

JAVA容器小结

JAVA中的容器提供了非常完善的方法来保存对象,你可以使用这些工具来解决大数据量的问题.下面是笔者在开发中用到过的一些容器总结如此. 1 ArrayList 使用ArrayList非常简单:创建一个实例,用add()插入对象,然后用get()访问这些对象,此时需要索引,就象数组一样,但是不需要方括号,ArrayList还有size()方法,从而可以知道ArrayList的大小,也可以避免因为越界而引发错误.另外,ArrayList长于随机访问元素,但是在List的中间插入和移处元素时较慢.下面是

Java 容器 &amp; 泛型:一、认识容器

容器是Java语言学习中重要的一部分.泥瓦匠我的感觉是刚开始挺难学的,但等你熟悉它,接触多了,也就"顺理成章"地知道了.Java的容器类主要由两个接口派生而出:Collection和Map.   一.Collection vs Collections 首先,Collection 和 Collections 是两个不同的概念.之所以放在一起,是为了更好的比较.Collection是容器层次结构中根接口.而Collections是一个提供一些处理容器类静态方法的类. JDK不提供Colle

Java 容器 &amp; 泛型:二、ArrayList 、LinkedList和Vector比较

一.List回顾 序列(List),有序的Collection,正如它的名字一样,是一个有序的元素列表.确切的讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素.实现List的有:ArrayList.LinkedList.Vector.Stack等.值得一提的是,Vector在JDK1.1的时候就有了,而List在JDK1.2的时候出现,待会我们会聊到ArrayList和Vector的区别.  

java 容器 集合 用法

Set,List,Map,Vector,ArrayList的区别 JAVA的容器---List,Map,Set Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├HashMap └WeakHashMap Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些 Collecti