一、汇合框架图
简化图:
阐明:对于以上的框架图有如下几点阐明
1、所有汇合类都位于 java.util
包下。Java 的汇合类次要由两个接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 汇合框架的根接口,这两个接口又蕴含了一些子接口或实现类。
2、汇合接口:6 个接口(短虚线示意),示意不同汇合类型,是汇合框架的根底。
3、抽象类:5 个抽象类(长虚线示意),对汇合接口的局部实现。可扩大为自定义汇合类。
4、实现类:8 个实现类(实线示意),对接口的具体实现。
5、Collection 接口是一组容许反复的对象。
6、Set 接口继承 Collection,汇合元素不反复。
7、List 接口继承 Collection,容许反复,保护元素插入程序。
8、Map 接口是键-值对象,与 Collection 接口没有什么关系。
9、Set、List 和 Map 能够看做汇合的三大类:
- List 汇合是有序汇合,汇合中的元素能够反复,拜访汇合中的元素能够依据元素的索引来拜访。
- Set 汇合是无序汇合,汇合中的元素不能够反复,拜访汇合中的元素只能依据元素自身来拜访(也是汇合里元素不容许反复的起因)。
- Map 汇合中保留 Key-value 对模式的元素,拜访时只能依据每项元素的 key 来拜访其 value。
二、总体剖析
大抵阐明:
看下面的框架图,先抓住它的骨干,即 Collection 和 Map。
1、Collection 是一个接口,是高度形象进去的汇合,它蕴含了汇合的基本操作和属性。Collection 蕴含了 List 和 Set 两大分支。
- List 是一个有序的队列,每一个元素都有它的索引。第一个元素的索引值是 0。List 的实现类有 LinkedList, ArrayList, Vector, Stack。
- Set 是一个不容许有反复元素的汇合。Set 的实现类有 HastSet 和 TreeSet。HashSet 依赖于 HashMap,它实际上是通过 HashMap 实现的;TreeSet 依赖于 TreeMap,它实际上是通过 TreeMap 实现的。
2、Map 是一个映射接口,即 key-value 键值对。Map 中的每一个元素蕴含“一个 key”和“key 对应的 value”。AbstractMap 是个抽象类,它实现了 Map 接口中的大部分 API。而 HashMap,TreeMap,WeakHashMap 都是继承于 AbstractMap。Hashtable 尽管继承于 Dictionary,但它实现了 Map 接口。
3、接下来,再看 Iterator。它是 遍历汇合 的工具,即咱们通常通过 Iterator 迭代器来遍历汇合。咱们说 Collection 依赖于 Iterator,是因为 Collection 的实现类都要实现 iterator()函数,返回一个 Iterator 对象。ListIterator 是专门为遍历 List 而存在的。
4、再看 Enumeration,它是 JDK 1.0 引入的抽象类。作用和 Iterator 一样,也是遍历汇合;然而 Enumeration 的性能要比 Iterator 少。在下面的框图中,Enumeration 只能在 Hashtable, Vector, Stack 中应用。
5、最初,看 Arrays 和 Collections。它们是操作数组、汇合的两个工具类。
有了下面的整体框架之后,咱们接下来对每个类别离进行剖析。
三、Collection 接口
Collection 接口是解决对象汇合的根接口,其中定义了很多对元素进行操作的办法。Collection 接口有两个次要的子接口 List 和 Set,留神 Map 不是 Collection 的子接口,这个要牢记。
Collection 接口中的办法如下:
![img]
其中,有几个比拟罕用的办法,比方办法 add()增加一个元素到汇合中,addAll()将指定汇合中的所有元素增加到汇合中,contains()
办法检测汇合中是否蕴含指定的元素,toArray()办法返回一个示意汇合的数组。
另外,Collection 中有一个 iterator()
函数,它的作用是返回一个 Iterator 接口。通常,咱们通过 Iterator 迭代器来遍历汇合。ListIterator 是 List 接口所特有的,在 List 接口中,通过 ListIterator()
返回一个 ListIterator 对象。
Collection 接口有两个罕用的子接口,上面具体介绍。
1.List 接口
List 汇合代表一个有序汇合,汇合中每个元素都有其对应的顺序索引。List 汇合容许应用反复元素,能够通过索引来拜访指定地位的汇合元素。
List 接口继承于 Collection 接口,它能够定义一个容许反复的有序汇合。因为 List 中的元素是有序的,所以咱们能够通过应用索引(元素在 List 中的地位,相似于数组下标)来拜访 List 中的元素,这相似于 Java 的数组。
List 接口为 Collection 间接接口。List 所代表的是有序的 Collection,即它用某种特定的插入程序来保护元素程序。用户能够对列表中每个元素的插入地位进行准确地管制,同时能够依据元素的整数索引(在列表中的地位)拜访元素,并搜寻列表中的元素。实现 List 接口的汇合次要有:ArrayList、LinkedList、Vector、Stack
。
(1)ArrayList
ArrayList 是一个动静数组,也是咱们最罕用的汇合。它容许任何合乎规定的元素插入甚至包含 null。每一个 ArrayList 都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素一直减少,容器的大小也会随着减少。在每次向容器中减少元素的同时都会进行容量查看,当快溢出时,就会进行扩容操作。所以 如果咱们明确所插入元素的多少,最好指定一个初始容量值,防止过多的进行扩容操作而浪费时间、效率。
size、isEmpty、get、set、iterator
和 listIterator 操作都以固定工夫运行。add 操作以摊派的固定工夫运行,也就是说,增加 n 个元素须要 O(n) 工夫(因为要思考到扩容,所以这不只是增加元素会带来摊派固定工夫开销那样简略)。
ArrayList 擅长于随机拜访。同时 ArrayList 是非同步的。
(2)LinkedList
同样实现 List 接口的 LinkedList 与 ArrayList 不同,ArrayList 是一个动静数组,而 LinkedList 是一个双向链表 。所以它除了有 ArrayList 的基本操作办法外还额定提供了get,remove,insert
办法在 LinkedList 的首部或尾部。
因为实现的形式不同,LinkedList 不能随机拜访,它所有的操作都是要依照双重链表的须要执行。在列表中索引的操作将从结尾或结尾遍历列表(从凑近指定索引的一端)。这样做的益处就是能够通过较低的代价在 List 中进行插入和删除操作。
与 ArrayList 一样,LinkedList 也是非同步的。如果多个线程同时拜访一个 List,则必须本人实现拜访同步。一种解决办法是在创立 List 时结构一个同步的 List:
List list = Collections.synchronizedList(new LinkedList(...));
(3)Vector
与 ArrayList 类似,然而 Vector 是同步的。所以说Vector 是线程平安的动静数组。它的操作与 ArrayList 简直一样。
(4)Stack
Stack 继承自 Vector,实现一个 后进先出的堆栈。Stack 提供 5 个额定的办法使得 Vector 得以被当作堆栈应用。根本的 push 和 pop 办法,还有 peek 办法失去栈顶的元素,empty 办法测试堆栈是否为空,search 办法检测一个元素在堆栈中的地位。Stack 刚创立后是空栈。
2.Set 接口
Set 是一种不包含反复元素的 Collection。它维持它本人的外部排序,所以随机拜访没有任何意义。与 List 一样,它同样容许 null 的存在然而仅有一个。因为 Set 接口的特殊性,所有传入 Set 汇合中的元素都必须不同,同时要留神任何可变对象,如果在对汇合中元素进行操作时,导致e1.equals(e2)==true
,则必定会产生某些问题。Set 接口有三个具体实现类,别离是散列集 HashSet、链式散列集 LinkedHashSet 和树形集 TreeSet。
Set 是一种不蕴含反复的元素的 Collection,无序,即任意的两个元素 e1 和 e2 都有 e1.equals(e2)=false,Set 最多有一个 null 元素。
须要留神的是:尽管 Set 中元素没有程序,然而元素在 set 中的地位是由该元素的 HashCode 决定的,其具体位置其实是固定的。
此外须要阐明一点,在 set 接口中的不反复是有特殊要求的。
举一个例子:对象 A 和对象 B,原本是不同的两个对象,失常状况下它们是可能放入到 Set 外面的,然而如果对象 A 和 B 的都重写了 hashcode 和 equals 办法,并且重写后的 hashcode 和 equals 办法是雷同的话。那么 A 和 B 是不能同时放入到 Set 汇合中去的,也就是 Set 汇合中的去重和 hashcode 与 equals 办法间接相干。
为了更好地了解,请看上面的例子:
public class Test{public static void main(String[] args) {Set<String> set=new HashSet<String>();
set.add("Hello");
set.add("world");
set.add("Hello");
System.out.println("汇合的尺寸为:"+set.size());
System.out.println("汇合中的元素为:"+set.toString());
}
}
运行后果:
汇合的尺寸为:2
汇合中的元素为:[world, Hello]
剖析:因为 String 类中重写了 hashcode 和 equals 办法,用来比拟指向的字符串对象所存储的字符串是否相等。所以这里的第二个 Hello 是加不进去的。
再看一个例子:
public class TestSet {public static void main(String[] args){Set<String> books = new HashSet<String>();
// 增加一个字符串对象
books.add(new String("Struts2 权威指南"));
// 再次增加一个字符串对象,// 因为两个字符串对象通过 equals 办法比拟相等,所以增加失败,返回 false
boolean result = books.add(new String("Struts2 权威指南"));
System.out.println(result);
// 上面输入看到汇合只有一个元素
System.out.println(books);
}
}
运行后果:
false
[Struts2 权威指南]
阐明:程序中,book 汇合两次增加的字符串对象显著不是一个对象(程序通过 new 关键字来创立字符串对象),当应用 == 运算符判断返回 false,应用 equals 办法比拟返回 true,所以不能增加到 Set 汇合中,最初只能输入一个元素。
(1)HashSet
HashSet 是一个没有反复元素的汇合。它是由 HashMap 实现的,不保障元素的程序(这里所说的没有程序是指:元素插入的程序与输入的程序不统一),而且 HashSet 容许应用 null 元素。HashSet 是非同步的,如果多个线程同时拜访一个哈希 set,而其中至多一个线程批改了该 set,那么它必须放弃内部同步。HashSet 按 Hash 算法来存储汇合的元素,因而具备很好的存取和查找性能。
HashSet 的实现形式大抵如下,通过一个 HashMap 存储元素,元素是寄存在 HashMap 的 Key 中,而 Value 对立应用一个 Object 对象。
HashSet 应用和了解中容易呈现的误区:
a.HashSet 中寄存 null 值。HashSet 中是容许存入 null 值的,然而在 HashSet 中仅仅可能存入一个 null 值。
b.HashSet 中存储元素的地位是固定的。HashSet 中存储的元素的是无序的,这个没什么好说的,然而因为 HashSet 底层是基于 Hash 算法实现的,应用了 hashcode,所以 HashSet 中相应的元素的地位是固定的。
c. 必须小心操作可变对象 (Mutable Object
)。如果一个 Set 中的可变元素扭转了本身状态导致Object.equals(Object)=true
将导致一些问题。
(2)LinkedHashSet
LinkedHashSet 继承自 HashSet,其底层是 基于 LinkedHashMap 来实现的,有序,非同步。LinkedHashSet 汇合同样是依据元素的 hashCode 值来决定元素的存储地位,然而它同时应用链表保护元素的秩序。这样使得元素看起来像是以插入程序保留的,也就是说,当遍历该汇合时候,LinkedHashSet 将会以元素的增加程序拜访汇合的元素。
(3)TreeSet
TreeSet 是一个有序汇合,其底层是基于 TreeMap 实现的,非线程平安。TreeSet 能够确保汇合元素处于排序状态。TreeSet 反对两种排序形式,天然排序和定制排序,其中天然排序为默认的排序形式。当咱们结构 TreeSet 时,若应用不带参数的构造函数,则 TreeSet 的应用天然比拟器;若用户须要应用自定义的比拟器,则须要应用带比拟器的参数。
留神:TreeSet 汇合不是通过 hashcode 和 equals 函数来比拟元素的. 它是通过 compare 或者 comparaeTo 函数来判断元素是否相等.compare 函数通过判断两个对象的 id,雷同的 id 判断为反复元素,不会被退出到汇合中。
四、Map 接口
Map 与 List、Set 接口不同,它是由一系列键值对组成的汇合,提供了 key 到 Value 的映射。同时它也没有继承 Collection。在 Map 中它保障了 key 与 value 之间的一一对应关系。也就是说一个 key 对应一个 value,所以它 不能存在雷同的 key 值,当然 value 值能够雷同。
1.HashMap
以哈希表数据结构实现,查找对象时通过哈希函数计算其地位,它是为疾速查问而设计的,其外部定义了一个 hash 表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中寄存的索引,如果有抵触,则应用散列链表的模式将所有雷同哈希地址的元素串起来,可能通过查看 HashMap.Entry 的源码它是一个单链表构造。
2.LinkedHashMap
LinkedHashMap 是 HashMap 的一个子类,它保留插入的程序,如果须要输入的程序和输出时的雷同,那么就选用 LinkedHashMap。
LinkedHashMap 是 Map 接口的哈希表和链接列表实现,具备可预知的迭代程序。此实现提供所有可选的映射操作,并容许应用 null 值和 null 键。此类不保障映射的程序,特地是它不保障该程序恒久不变。
LinkedHashMap 实现与 HashMap 的不同之处在于,后者保护着一个运行于所有条目标双重链接列表。此链接列表定义了迭代程序,该迭代程序能够是插入程序或者是拜访程序。
依据链表中元素的程序能够分为:按插入程序的链表,和按拜访程序 (调用 get 办法) 的链表。默认是按插入程序排序,如果指定按拜访程序排序,那么调用 get 办法后,会将这次拜访的元素移至链表尾部,一直拜访能够造成按拜访程序排序的链表。
留神,此实现不是同步的。如果多个线程同时拜访链接的哈希映射,而其中至多一个线程从构造上批改了该映射,则它必须放弃内部同步。因为 LinkedHashMap 须要保护元素的插入程序,因而性能略低于 HashMap 的性能,但在迭代拜访 Map 里的全副元素时将有很好的性能,因为它以链表来保护外部程序。
3.TreeMap
TreeMap 是一个有序的 key-value 汇合,非同步,基于红黑树(Red-Black tree)实现,每一个 key-value 节点作为红黑树的一个节点。TreeMap 存储时会进行排序的,会依据 key 来对 key-value 键值对进行排序,其中排序形式也是分为两种,一种是天然排序,一种是定制排序,具体取决于应用的构造方法。
天然排序:TreeMap 中所有的 key 必须实现 Comparable 接口,并且所有的 key 都应该是同一个类的对象,否则会报 ClassCastException 异样。
定制排序:定义 TreeMap 时,创立一个 comparator 对象,该对象对所有的 treeMap 中所有的 key 值进行排序,采纳定制排序的时候不须要 TreeMap 中所有的 key 必须实现 Comparable 接口。
TreeMap 判断两个元素相等的规范:两个 key 通过 compareTo()
办法返回 0,则认为这两个 key 相等。
如果应用自定义的类来作为 TreeMap 中的 key 值,且想让 TreeMap 可能良好的工作,则必须重写自定义类中的 equals()
办法,TreeMap 中判断相等的规范是:两个 key 通过 equals()
办法返回为 true,并且通过 compareTo()
办法比拟应该返回为 0。
五、Iterator 与 ListIterator 详解
1.Iterator
Iterator 的定义如下:
public interface Iterator<E> {}
Iterator 是一个接口,它是汇合的迭代器。汇合能够通过 Iterator 去遍历汇合中的元素。
Iterator 提供的 API 接口如下:
- boolean hasNext():判断汇合里是否存在下一个元素。如果有,hasNext()办法返回 true。
- Object next():返回汇合里下一个元素。
- void remove():删除汇合里上一次 next 办法返回的元素。
应用示例:
public class IteratorExample {public static void main(String[] args) {ArrayList<String> a = new ArrayList<String>();
a.add("aaa");
a.add("bbb");
a.add("ccc");
System.out.println("Before iterate :" + a);
Iterator<String> it = a.iterator();
while (it.hasNext()) {String t = it.next();
if ("bbb".equals(t)) {it.remove();
}
}
System.out.println("After iterate :" + a);
}
}
输入后果如下:
Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc]
留神:
- Iterator 只能单向挪动。
- Iterator.remove()是惟一平安的形式来在迭代过程中批改汇合;如果在迭代过程中以任何其它的形式批改了根本汇合将会产生未知的行为。而且每调用一次
next()
办法,remove()
办法只能被调用一次,如果违反这个规定将抛出一个异样。
2.ListIterator
ListIterator 是一个性能更加弱小的迭代器, 它继承于 Iterator 接口,只能用于各种 List 类型的拜访。能够通过调用 listIterator()
办法产生一个指向 List 开始处的 ListIterator, 还能够调用 listIterator(n)
办法创立一个一开始就指向列表索引为 n 的元素处的 ListIterator。
ListIterator 接口定义如下:
public interface ListIterator<E> extends Iterator<E> {boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
}
由以上定义咱们能够推出 ListIterator 能够:
- 双向挪动(向前 / 向后遍历).
- 产生绝对于迭代器在列表中指向的以后地位的前一个和后一个元素的索引.
- 能够应用
set()
办法替换它拜访过的最初一个元素. - 能够应用
add()
办法在next()
办法返回的元素之前或previous()
办法返回的元素之后插入一个元素.
应用示例:
public class ListIteratorExample {public static void main(String[] args) {ArrayList<String> a = new ArrayList<String>();
a.add("aaa");
a.add("bbb");
a.add("ccc");
System.out.println("Before iterate :" + a);
ListIterator<String> it = a.listIterator();
while (it.hasNext()) {System.out.println(it.next() + "," + it.previousIndex() + "," + it.nextIndex());
}
while (it.hasPrevious()) {System.out.print(it.previous() + " ");
}
System.out.println();
it = a.listIterator(1);
while (it.hasNext()) {String t = it.next();
System.out.println(t);
if ("ccc".equals(t)) {it.set("nnn");
} else {it.add("kkk");
}
}
System.out.println("After iterate :" + a);
}
}
输入后果如下:
Before iterate : [aaa, bbb, ccc]
aaa, 0, 1
bbb, 1, 2
ccc, 2, 3
ccc bbb aaa
bbb
ccc
After iterate : [aaa, bbb, kkk, nnn]
六、异同点
1.ArrayList 和 LinkedList
- ArrayList 是实现了基于动静数组的数据结构,LinkedList 基于链表的数据结构。
- 对于随机拜访 get 和 set,ArrayList 相对优于 LinkedList,因为 LinkedList 要挪动指针。
- 对于新增和删除操作 add 和 remove,LinedList 比拟占优势,因为 ArrayList 要挪动数据。
这一点要看理论状况的。若只对单条数据插入或删除,ArrayList 的速度反而优于 LinkedList。但若是批量随机的插入删除数据,LinkedList 的速度大大优于 ArrayList. 因为 ArrayList 每插入一条数据,要挪动插入点及之后的所有数据。
2.HashTable 与 HashMap
相同点:
- 都实现了
Map、Cloneable、java.io.Serializable
接口。 - 都是存储 ” 键值对(key-value)” 的散列表,而且都是采纳拉链法实现的。
不同点:
(1)历史起因:HashTable 是基于古老的 Dictionary 类的,HashMap 是 Java 1.2 引进的 Map 接口的一个实现。
(2)同步性:HashTable 是线程平安的,也就是说是同步的,而 HashMap 是线程序不平安的,不是同步的。
(3)对 null 值的解决:HashMap 的 key、value 都可为 null,HashTable 的 key、value 都不可为 null。
(4)基类不同:HashMap 继承于 AbstractMap,而 Hashtable 继承于 Dictionary。
- Dictionary 是一个抽象类,它间接继承于 Object 类,没有实现任何接口。Dictionary 类是 JDK 1.0 的引入的。尽管 Dictionary 也反对“增加 key-value 键值对”、“获取 value”、“获取大小”等基本操作,但它的 API 函数比 Map 少;而且 Dictionary 个别是通过 Enumeration(枚举类)去遍历,Map 则是通过 Iterator(迭代 M 器)去遍历。然而因为 Hashtable 也实现了 Map 接口,所以,它即反对 Enumeration 遍历,也反对 Iterator 遍历。
- AbstractMap 是一个抽象类,它实现了 Map 接口的绝大部分 API 函数;为 Map 的具体实现类提供了极大的便当。它是 JDK 1.2 新增的类。
(5)反对的遍历品种不同:HashMap 只反对 Iterator(迭代器)遍历。而 Hashtable 反对 Iterator(迭代器)和 Enumeration(枚举器)两种形式遍历。
3.HashMap、Hashtable、LinkedHashMap 和 TreeMap 比拟
Hashmap 是一个最罕用的 Map,它依据键的 HashCode 值存储数据,依据键能够间接获取它的值,具备很快的访问速度。遍历时,获得数据的程序是齐全随机的。HashMap 最多只容许一条记录的键为 Null;容许多条记录的值为 Null;HashMap 不反对线程的同步,即任一时刻能够有多个线程同时写 HashMap;可能会导致数据的不统一。如果须要同步,能够用 Collections 的 synchronizedMap 办法使 HashMap 具备同步的能力。
Hashtable 与 HashMap 相似,不同的是:它不容许记录的键或者值为空;它反对线程的同步,即任一时刻只有一个线程能写 Hashtable,因而也导致了 Hashtale 在写入时会比较慢。
LinkedHashMap 保留了记录的插入程序,在用 Iterator 遍历 LinkedHashMap 时,先失去的记录必定是先插入的,也能够在结构时用带参数,依照利用次数排序。在遍历的时候会比 HashMap 慢,不过有种状况例外,当 HashMap 容量很大,理论数据较少时,遍历起来可能会比 LinkedHashMap 慢,因为 LinkedHashMap 的遍历速度只和理论数据无关,和容量无关,而 HashMap 的遍历速度和他的容量无关。
如果须要输入的程序和输出的雷同,那么用 LinkedHashMap 能够实现,它还能够按读取程序来排列,像连接池中能够利用。LinkedHashMap 实现与 HashMap 的不同之处在于,后者保护着一个运行于所有条目标双重链表。此链接列表定义了迭代程序,该迭代程序能够是插入程序或者是拜访程序。对于 LinkedHashMap 而言,它继承与 HashMap、底层应用哈希表与双向链表来保留所有元素。其基本操作与父类 HashMap 类似,它通过重写父类相干的办法,来实现本人的链接列表个性。
TreeMap 实现 SortMap 接口,外部实现是红黑树。可能把它保留的记录依据键排序,默认是按键值的升序排序,也能够指定排序的比拟器,当用 Iterator 遍历 TreeMap 时,失去的记录是排过序的。TreeMap 不容许 key 的值为 null。非同步的。
个别状况下,咱们用的最多的是 HashMap,HashMap 外面存入的键值对在取出的时候是随机的,它依据键的 HashCode 值存储数据,依据键能够间接获取它的值,具备很快的访问速度。在 Map 中插入、删除和定位元素,HashMap 是最好的抉择。
TreeMap 取出来的是排序后的键值对。但如果您要按天然程序或自定义程序遍历键,那么 TreeMap 会更好。
LinkedHashMap 是 HashMap 的一个子类,如果须要输入的程序和输出的雷同,那么用 LinkedHashMap 能够实现,它还能够按读取程序来排列,像连接池中能够利用。
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.TreeMap;
public class MapTest {public static void main(String[] args) {
//HashMap
HashMap<String,String> hashMap = new HashMap();
hashMap.put("4", "d");
hashMap.put("3", "c");
hashMap.put("2", "b");
hashMap.put("1", "a");
Iterator<String> iteratorHashMap = hashMap.keySet().iterator();
System.out.println("HashMap-->");
while (iteratorHashMap.hasNext()){Object key1 = iteratorHashMap.next();
System.out.println(key1 + "--" + hashMap.get(key1));
}
//LinkedHashMap
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
linkedHashMap.put("4", "d");
linkedHashMap.put("3", "c");
linkedHashMap.put("2", "b");
linkedHashMap.put("1", "a");
Iterator<String> iteratorLinkedHashMap = linkedHashMap.keySet().iterator();
System.out.println("LinkedHashMap-->");
while (iteratorLinkedHashMap.hasNext()){Object key2 = iteratorLinkedHashMap.next();
System.out.println(key2 + "--" + linkedHashMap.get(key2));
}
//TreeMap
TreeMap<String,String> treeMap = new TreeMap();
treeMap.put("4", "d");
treeMap.put("3", "c");
treeMap.put("2", "b");
treeMap.put("1", "a");
Iterator<String> iteratorTreeMap = treeMap.keySet().iterator();
System.out.println("TreeMap-->");
while (iteratorTreeMap.hasNext()){Object key3 = iteratorTreeMap.next();
System.out.println(key3 + "--" + treeMap.get(key3));
}
}
}
输入后果:
HashMap-->
3--c
2--b
1--a
4--d
LinkedHashMap-->
4--d
3--c
2--b
1--a
TreeMap-->
1--a
2--b
3--c
4--d
4.HashSet、LinkedHashSet、TreeSet 比拟
Set 接口
Set 不容许蕴含雷同的元素,如果试图把两个雷同元素退出同一个汇合中,add 办法返回 false。
Set 判断两个对象雷同不是应用 == 运算符,而是依据 equals 办法。也就是说,只有两个对象用 equals 办法比拟返回 true,Set 就不会承受这两个对象。
HashSet
HashSet 有以下特点:
- 不能保障元素的排列程序,程序有可能发生变化。
- 不是同步的。
- 汇合元素能够是 null,但只能放入一个 null。
当向 HashSet 联合中存入一个元素时,HashSet 会调用该对象的 hashCode()办法来失去该对象的 hashCode 值,而后依据 hashCode 值来决定该对象在 HashSet 中存储地位。简略的说,HashSet 汇合判断两个元素相等的规范是两个对象通过 equals 办法比拟相等,并且两个对象的 hashCode()办法返回值也相等。
留神,如果要把一个对象放入 HashSet 中,重写该对象对应类的 equals 办法,也应该重写其 hashCode()办法。其规定是如果两个对象通过 equals 办法比拟返回 true 时,其 hashCode 也应该雷同。另外,对象中用作 equals 比拟规范的属性,都应该用来计算 hashCode 的值。
LinkedHashSet
LinkedHashSet 汇合同样是依据元素的 hashCode 值来决定元素的存储地位,然而它同时应用链表保护元素的秩序。这样使得元素看起来像是以插入程序保留的,也就是说,当遍历该汇合时候,LinkedHashSet 将会以元素的增加程序拜访汇合的元素。
LinkedHashSet 在迭代拜访 Set 中的全副元素时,性能比 HashSet 好,然而插入时性能略微逊色于 HashSet。
TreeSet 类
TreeSet 是 SortedSet 接口的惟一实现类,TreeSet 能够确保汇合元素处于排序状态。TreeSet 反对两种排序形式,天然排序和定制排序,其中天然排序为默认的排序形式。向 TreeSet 中退出的应该是同一个类的对象。
TreeSet 判断两个对象不相等的形式是两个对象通过 equals 办法返回 false,或者通过 CompareTo 办法比拟没有返回 0。
天然排序
天然排序应用要排序元素的 CompareTo(Object obj)
办法来比拟元素之间大小关系,而后将元素依照升序排列。
Java 提供了一个 Comparable 接口,该接口里定义了一个
compareTo(Object obj)
办法,该办法返回一个整数值,实现了该接口的对象就能够比拟大小。obj1.compareTo(obj2)
办法如果返回 0,则阐明被比拟的两个对象相等,如果返回一个负数,则表明 obj1 大于 obj2,如果是正数,则表明 obj1 小于 obj2。如果咱们将两个对象的 equals 办法总是返回 true,则这两个对象的 compareTo 办法返回应该返回 0。
定制排序
天然排序是依据汇合元素的大小,以升序排列,如果要定制排序,应该应用 Comparator 接口,实现 int compare(T o1,T o2)
办法。
package com.test;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
/**
* @description 几个 set 的比拟
* HashSet:哈希表是通过应用称为散列法的机制来存储信息的,元素并没有以某种特定程序来寄存;* LinkedHashSet:以元素插入的程序来保护汇合的链接表,容许以插入的程序在汇合中迭代;* TreeSet:提供一个应用树结构存储 Set 接口的实现,对象以升序顺序存储,拜访和遍历的工夫很快。* @author Zhou-Jingxian
*
*/
public class SetDemo {public static void main(String[] args) {HashSet<String> hs = new HashSet<String>();
hs.add("B");
hs.add("A");
hs.add("D");
hs.add("E");
hs.add("C");
hs.add("F");
System.out.println("HashSet 程序:\n"+hs);
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("B");
lhs.add("A");
lhs.add("D");
lhs.add("E");
lhs.add("C");
lhs.add("F");
System.out.println("LinkedHashSet 程序:\n"+lhs);
TreeSet<String> ts = new TreeSet<String>();
ts.add("B");
ts.add("A");
ts.add("D");
ts.add("E");
ts.add("C");
ts.add("F");
System.out.println("TreeSet 程序:\n"+ts);
}
}
输入后果:
HashSet 程序:[D, E, F, A, B, C]
LinkedHashSet 程序:[B, A, D, E, C, F]
TreeSet 程序:[A, B, C, D, E, F]
5、Iterator 和 ListIterator 区别
咱们在应用 List,Set 的时候,为了实现对其数据的遍历,咱们常常应用到了 Iterator(迭代器)。应用迭代器,你不须要干预其遍历的过程,只须要每次取出一个你想要的数据进行解决就能够了。然而在应用的时候也是有不同的。
List 和 Set 都有 iterator()
来获得其迭代器。对 List 来说,你也能够通过 listIterator()获得其迭代器,两种迭代器在有些时候是不能通用的,Iterator 和 ListIterator 次要区别在以下方面:
- ListIterator 有
add()
办法,能够向 List 中增加对象,而 Iterator 不能 - ListIterator 和 Iterator 都有
hasNext()
和next()
办法,能够实现程序向后遍历,然而 ListIterator 有hasPrevious()
和previous()
办法,能够实现逆向(程序向前)遍历。Iterator 就不能够。 - ListIterator 能够定位以后的索引地位,
nextIndex()
和previousIndex()
能够实现。Iterator 没有此性能。 - 都可实现删除对象,然而 ListIterator 能够实现对象的批改,
set()
办法能够实现。Iierator 仅能遍历,不能批改。
因为 ListIterator 的这些性能,能够实现对 LinkedList 等 List 数据结构的操作。其实,数组对象也能够用迭代器来实现。
6、Collection 和 Collections 区别
(1)java.util.Collection
是一个汇合接口(汇合类的一个顶级接口)。它提供了对汇合对象进行基本操作的通用接口办法。Collection 接口在 Java 类库中有很多具体的实现。Collection 接口的意义是为各种具体的汇合提供了最大化的对立操作形式,其间接继承接口有 List 与 Set。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
(2)java.util.Collections 是一个包装类(工具类 / 帮忙类)。它蕴含有各种无关汇合操作的动态多态办法。此类不能实例化,就像一个工具类,用于对汇合中元素进行排序、搜寻以及线程平安等各种操作,服务于 Java 的 Collection 框架。
代码示例:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {public static void main(String args[]) {
// 留神 List 是实现 Collection 接口的
List list = new ArrayList();
double array[] = { 112, 111, 23, 456, 231};
for (int i = 0; i < array.length; i++) {list.add(new Double(array[i]));
}
Collections.sort(list);
for (int i = 0; i < array.length; i++) {System.out.println(list.get(i));
}
// 后果:23.0 111.0 112.0 231.0 456.0
}
}
文末福利
肝了全网,43 份 Java 思维导图,须要自取!!!
《Java 面试手册》V1.0 版本,高清 PDF 收费获取