线性表, 是常用的数据结构之一, 链表, 同样是常用的数据结构, 哈希表, 也是常用的数据结构, 在开展Java开发之际, JDK已然为我们给出了一系列相应的类, 用以实现基本的数据结构, 这些类全都在java.util包里面, 本文尝试借助简单的描述, 朝着读者阐释各个类的作用, 以及怎样正确运用这些类。

├List

│├

│├

│└

│ └Stack

└Set

Map

├

├

└

接口

是最为基础的集合接口, 一个用来代表一组, 也就是的元素()。一部分允许存在相同元素然而另一部分不可以这样。一部分能够进行排序可是另一部分不具备这种能力。Java SDK并未提供直接继承自的类, Java SDK所提供的类全都是继承自的“子接口”就像List和Set。

所有实现接口的类, 都必须提供两个标准构造函数, 其中一个是无参数的构造函数, 它用于创建一个空的, 另一个是有一个参数的构造函数, 它用于创建一个新的, 这个新的与传入的有相同的元素, 而后一个构造函数允许用户复制一个。

怎样去遍历其中的每一个元素呢, 不管其实际的类型所呈现出怎样的情况, 它都对一个有着特定形式的方法予以支持, 此方法会返回一个迭代子, 借助这个迭代子就能够逐个地去访问其中的每一个元素, 典型的运用方式是像下面这样: (运用迭代器)

Iterator it = collection.iterator(); // 获得一个迭代子  
while(it.hasNext()) {  
    Object obj = it.next(); // 得到下一个元素
}

由接口派生的两个接口是List和Set。

主要方法:

List接口(有序,允许重复)

List具备有序性的特点, 借助此接口能够精准地把控每一个元素插入时所处的位置。用户可以运用索引(该索引是指元素于List里的位置, 与数组下标相类似)来对List当中的元素予以访问, 这种情况跟Java的数组颇为相似。

和下面要提到的Set不同,List允许有相同的元素。

除去带有接口所必定存在的那个规定方法外, List另外给出一个特定方法,这个特定方法返回的是某个新接口, 该新接口与标准的接口相较而言, 含有着些诸如向新接口里面进行特定元素添加、从新接口内部除去特定元素、在新接口里设定特定元素的方法, 而且新接口有能力让程序对其内部所能容纳的各类元素开展向前遍历的功能, 或者是开展向后遍历的功能。

实现List接口的常用类有,,和Stack。

主要方法:

它达成了List这个接口, 是准许null元素存在的情况之下做到这一点的。此外呢它还供给了更多的get, 方法用于对应的首部或者尾部这件事情上。正是凭借这些操作, 才致使它能够像是那些会被拿来当成堆栈, 再不然拿来如队列, 又或者拿来充当双向队列那样。

留意不存在同步的办法, 要是多个线程一块儿去访问一个List, 那么必然要自行去实现对于访问的同步, 有一种解决的办法是当创建List的时候去构造出一个同步的List。

List list = Collections.synchronizedList(new LinkedList(...)); 

java.util包 数据结构 Collection 接口 List Set Map 接口 HashMap Hashtable WeakHashMap_java 数据结构

将可变大小的数组给实现出来了, 它对所有元素都予以允许, 其中这所有元素里面还包含null, 并且它不存在同步的情况。

size方法的运行时间是常数, , get方法的运行时间是常数, , set方法的运行时间是常数。然而, add方法的开销是分摊的常数,, 增添n个元素需要O(n)的时间。除此之外的其他方法运行时间是线性。

每个实例都存在一个容量, 此容量是用于存储元素的数组的大小, 该容量能够随着持续增添新元素而自主增加, 然而增长算法并未被定义, 当有大量元素需要插入时, 在插入以前可以调用方法以让增加的容量提升插入效率。

和一样,也是非同步的()。

主要方法:

非常类似,但是是同步的。

被创建出来的那个, 虽说跟创建出来的属于同一接口, 然而呢是因为它为同步情况, 当有一个被创建出来并且正处于被使用的状态时, 另一个线程把它的状态给改变了(比如说添加或者删除了一些元素), 在这个时候要是去调用它的相应方法的话就会抛出异常, 所以必须得捕获这个异常。

Stack 类

Stack继承自, 去实现一个呈现后进先出特性的堆栈, Stack提供5个额外可用的方法, 据此得以被当作堆栈来使用, 有基本的push? 和 和和去push方法, 及pop方法 , 还有peek那个方法, 通过此方法能得到处于栈顶位置的元素 , 有能用来检验堆栈是否为空的empty方法 , 有能去检测一个元素在该堆栈里具体位置的方法 , Stack该堆栈 在刚刚创建完成之后它被明确为空栈。

Set接口(常采用set去除list里的重复元素)

Set是这样一种集合, 这种集合不包含重复的元素, 也就是说, 对于任意两个元素e1和e2而言, 都存在e1.(e2)=false的情况, 并且Set最多会有一个null元素。

显然, Set的构造函数存在一个限制条件, 即所传入的参数之中不可以含有重复的元素。

要留意: 务必要谨慎去操作可变对象()。要是一位Set当中的可变元素更改了自身状态致使. () = true将会引发某些问题。

Map接口

请留意, Map并非继承特定接口, Map能够实现从key到value的映射关系。在一个Map里面, 是不允许出现相同的key的, 并且每个key仅仅映照一个value。Map接口会提供3种关于集合的视图, Map的内容能够被视作一组key集合, 一组是value集合, 或者是一组关于key-value的映射。

Map被用于保存有着映射关系的数据, Map之中保存有两组数据, 分别是key和value, 它们二者均能够是任意引用类型的数据, 然而key不可以重复, 所以靠着指定的key便能够取出对应的value, Map接口定义了如下这般常用的方法:

接手Map接口, 去达到一个key-value映射的哈希表的目标。任何并非是空物(non-null)的对象都能够当作key或者用作value处。

使用put(key, value)来添加数据, 使用get(key)来取出数据, 这两个基本操作的时间开销是常数。

依据和load这两个参数来对性能予以调整, 一般而言, 缺省状态下的load为0.75, 它比较出色地达成了时间与空间的平衡, 要是增大load, 那么能够节约空间, 不过相应的查找时间就会增大,而这会对诸如get以及put这类操作产生影响。

以下为使用的简单示例, 把1放置入相关处, 把2放置入相关处, 把3放置入相关处, 其中, 它们的key分别是”one”, 又分别是”two”, 还分别是”three”:

Hashtable numbers = new Hashtable();  
numbers.put(“one”, new Integer(1));  
numbers.put(“two”, new Integer(2));  
numbers.put(“three”, new Integer(3)); 

要取出一个数,比如2,用相应的key:

Integer n = (Integer)numbers.get(“two”);

因作为key的对象, 会借计算自个散列函数, 来判定与之对应的value的位置, 所以任何充当key的对象, 都得实现相关方法。按照散列函数的定义, 和方法继承自根类, 若将自定义的类用为key, 需格外小心, 若两个对象相同, 也就是obj1.(obj2)=true, 那么它们的必须一样, 不过若两个对象不同, 其间的不一定不一样,假使两个不同对象的相同, 这般现象称作冲突, 冲突会使得操作哈希表的时间开销变大, 因而要尽量定义优良的()方法, 这能够加快哈希表的操作。

要是相同的对象存在不同的情况, 针对哈希表的操作就将会诞生超乎预料的后果(原本期待的get方法返回值是null), 若是想规避此等问题的发生, 仅仅需要牢牢记住这么一条: 既要同时复写一种方法且复写另一种方法, 可千万别只去写其中的某一个。

是同步的。

什么和类似, 不同之处在哪里, 在于它是非同步的, 并且它允许null, 也就是null value和null key。然而当把它视为是那样的时候(()方法能够返回其相应情况), 其迭代子操作时间开销会和它的容量成比例。所以, 如果迭代操作的性能相当重要的话, 就不要把它的初始化容量设置得过高, 或者把load设置得过低。

与的区别:

1、 嗯……这个……同步性方面, 存在这样的情况, 它有着同步的特性, 在这个类里头, 其中一些方法能够确保处于这儿的对象具备线程安全的属性。然而, 还有另外一种情况却是异步的,所以在此之中的对象并不具备线程安全的特性。由于同步所提出的要求会对执行的效率产生影响, 所以, 要是你并不需要具备线程安全特性的集合, 那么选用它会是一个相当不错的选择, 如此一来便能够规避掉因为同步而导致的那些不必要的性能开销, 进而提升效率。

2、 值: 能够使你把空值用作一个表的条目的键或者值, 然而不存在放入空值这一情况。最多仅有一个键值为null, 不过可以拥有无数多个值是null。

注意:

1、用作key的对象必须实现和方法。

2、不能保证其中的键值对的顺序

3、尽量不要使用可变对象作为它们的key值。

这是一种经过改进的情况, 它针对 key 采用所谓的“弱引用”的方式。要是一个 key 不会再被外部引用, 那么这个 key 就能够被 GC 回收。

与的用法大体上是基本一致的, 然而它们之间存在着区别, 具体体现为: 在后者的情况当中, 其key会保留对象的那种强引用, 也就是说, 只要对象没有被销毁掉, 那么该对象所有key所引用的对象就不会被进行垃圾回收操作, 而且也不会自动去删除这些key所对应的键值对对象。但是, 要是的key所引用的对象并没有被其他强引用变量所引用, 那么这些key所引用的对象就存在着可能会被回收的情况了。在的情形里, 其中的每个key对象保存的是实际对象的弱引用, 当实际对象被回收之后, 就会自动删除该key所对应的键值对。

public static void main(String[] args) {
    WeakHashMap w1=new WeakHashMap();
    //添加三个键值对,三个key都是匿名字符串,没有其他引用
    w1.put("语文", "良好");
    w1.put("数学", "及格");
    w1.put("英语", "中等");
    w1.put("java", "good");//该key是一个系统缓存的字符串对象
    System.out.println(w1 );//输出{java=good, 数学=及格, 英语=中等, 语文=良好}
    //通知系统进行垃圾回收
    System.gc();
    System.runFinalization();
    System.out.println(w1 );//输出{java=good},没有其他引用的键值对都被回收
}

有一个从Map接口派生出来的子接口, 存在着一个实现类, 这个实现类是基于红黑树来对所有的key进行排序的, 并且有着两种排序方式, 分别是自然排序以及定制排序。

那key是以那种形式来进行存储的, 对于key所提出的要求跟对于元素所给出的要求基本上是保持一致的。

1、Map.Entry(), 返回那个最小key所对应的键值对, 要是Map为空的话, 那就返回null。

2、 ():返回最小key,如果为空,则返回null。

3、按照Map.Entry ()的规则, 返回那个由最大key所对应的, 键值对, 要是Map为空的话, 这么做就会返回null。

4、 ():返回最大key,如果为空,则返回null。

5、Map进行Entry操作, 针对其中的key, 返回与处于其后面一位存在关联的键值对, 要是这种关联不存在也就是为空了, 那么返回的结果就是null。

6、Map所对应的Entry当中返回处于关键前面一位的键值对, 若为空的情况下那就将会返回值为null。

7、 ( key):返回处于key之前一位的key值, 要是为空的状况, 那么就返回null。

8、 返回, 该Map的, 子Map, 其key范围, 从, 到toKey。

9、 返回, 该Map的子Map, 这个子Map, 其key的范围, 是从呐(那是包括的性质噻)到tokey呐(这儿是不包括的那种呀)。

10、 返回自该Map的子Map, 其key范围大于, 大于的范围含不含取决于第二个参数, 所有大于该范围的key所构成的子Map。

11、  获取该Map的子Map, 该子Map包含所有key,这些key使得按此种方式确定的key范围小于tokey, 而此key范围是否包括tokey取决于第二个参数。

总结

要是关联到堆栈、队列这类操作, 那就得思索选用List, 要是存在着需要能迅速地插入、删除元素的情况, 那就应当采用, 要是有着需要能快速地随机访问元素的状况, 那就应该选择使用。

倘若程序处于单线程环境里, 或者访问仅仅于一个线程当中开展, 那就考虑非同步的类, 其有着较高的效率, 要是多个线程有可能同时对一个类实施操作, 那么应当采用同步的类。

务必格外留意针对哈希表的操作, 充当key的对象得正确地进行复写以及方法!

尽可能地返回接口, 而不是实际的类型, 比如说返回List, 而不是其他的类型, 如此这般, 如果往后需要将其换成另外一种类型的时候, 客户端代码并不需要进行改变, 这便是针对抽象进行编程。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐