集合类是Java中一项很有用的工具类,它很像数组但又远远超过数组。它不仅可以存储数量不等的多个对象还可以实现不同的数据结构!
其中集合主要分为Set、List、Map三个体系!
简单说明
Java API中的位置:java.util。
主要分类
本图展示了主要的结构图。集合主要由两个接口派生:Collection
与Map
。
Collection:Set与List的父接口,其继承了Iterable接口(用于迭代,其内部只有一个方法iterator)。
Set:无序集合,内部数据不可重复。查找数据主要依赖于元素本身数据内容。
List:有序集合,内部数据可重复。查找数据主要依赖于元素索引。
Map:具有映射关系(key-value对)的集合,内部数据查找主要依赖于key值,因此key值不可重复,但数据内容可重复。
关于Iterator与Iterable的联系与区别,请自行搜索,我们使用时一般认为集合用Iterator来迭代
另外,关于子接口:
ArrayList:使用数组结构存储,可变长,查询快,增删慢,线程不同步
LinkedList:使用链表数组结构存储,查询慢,增删快
Vector:最先出现的集合,与ArrayList相同,但线程同步,速度慢(弃)
Collection中主要的方法
集合的成员方法从根本上来说就是对数据进行增删改查,但是因为各个集合内部数据结构各异,因此其增删改查的内部实现原理不同。
API中collection的方法
Modifier and Type | Method and Description |
---|---|
boolean add(E e) | Ensures that this collection contains the specified element (optional operation). |
boolean addAll(Collection c) | Adds all of the elements in the specified collection to this collection (optional operation). |
void clear() | Removes all of the elements from this collection (optional operation). |
boolean contains(Object o) | Returns true if this collection contains the specified element. |
boolean containsAll(Collection c) | Returns true if this collection contains all of the elements in the specified collection. |
boolean equals(Object o) | Compares the specified object with this collection for equality. |
int hashCode() | Returns the hash code value for this collection. |
boolean isEmpty() | Returns true if this collection contains no elements. |
Iterator iterator() | Returns an iterator over the elements in this collection. |
default Stream parallelStream() | Returns a possibly parallel Stream with this collection as its source. |
boolean remove(Object o) | Removes a single instance of the specified element from this collection, if it is present (optional operation). |
boolean removeAll(Collection c) | Removes all of this collection’s elements that are also contained in the specified collection (optional operation). |
default boolean removeIf(Predicate filter) | Removes all of the elements of this collection that satisfy the given predicate. |
boolean retainAll(Collection c) | Retains only the elements in this collection that are contained in the specified collection (optional operation). |
int size() | Returns the number of elements in this collection. |
default Spliterator spliterator() | Creates a Spliterator over the elements in this collection. |
default Stream stream() | Returns a sequential Stream with this collection as its source. |
Object[] toArray() | Returns an array containing all of the elements in this collection. |
T[] toArray(T[] a) | Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array. |
代码示例
这里使用ArrayList来完成Collection的成员方法的演示。
import java.util.*;
public class CollectionDemo {
public static void print(Object o) {
System.out.println(o);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList al1 = new ArrayList();
ArrayList al2 = new ArrayList();
// 1、添加元素
al1.add("Hello");
al1.add("World");
al1.add("Java");
al2.add("Hello");
al2.add("World");
al2.add("Java");
print("al1中存在的元素:" + al1);
// 2、删除元素
al1.remove("Java");
print("删除了元素的al1为:" + al1);
// 3、判断是否为空
print("al1是否为空?" + al1.isEmpty());
// 4、是否含有指定元素
print("al1是否含有Hello元素?" + al1.contains("Hello"));
// 5、元素个数
print("al1中的元素个数:" + al1.size());
// 6、只保留与指定集合的交集
al1.retainAll(al2);
print(al1);
// 7、移除指定集合也有的全部元素
al1.removeAll(al2);
print(al1);
// 8、添加指定集合的所有元素
al1.addAll(al2);
print(al1);
// 9、将集合转换成Object型数组
print(al1.toArray());
// 10、指定对象与之是否相等
print("al1是否和al2相等?" + al1.equals(al2));
// 11、是否包含指定集合的所有元素
print("al1是否包含al2的所有元素:" + al1.containsAll(al2));
// 12、移除所有元素
al1.clear();
print("al1移除所有元素后:" + al1);
}
}
运行结果
al1中存在的元素:[Hello, World, Java]
删除了元素的al1为:[Hello, World]
al1是否为空?false
al1是否含有Hello元素?true
al1中的元素个数:2
[Hello, World]
[]
[Hello, World, Java]
[Ljava.lang.Object;@15db9742
al1是否和al2相等?true
al1是否包含al2的所有元素?true
al1移除所有元素后:[]
说明:代码第七行@SuppressWarnings("unchecked")
是Java中的注解,此处主要是抑制有关”不受检查异常”的警告信息!如果没有此行代码,编译时将会出现以下信息(当然也可以不加此代码并人为忽略此信息)。
$ javac CollectionDemo.java -encoding utf8
注: CollectionDemo.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
如果你使用的是eclipse,将会跳过编译结果显示,直接生成运行结果。
使用Iterator遍历集合元素
Iterator接口主要有三种方法
Modifier and Type | Method and Description |
---|---|
boolean hasNext() | 如果被迭代集合中还有元素没有被遍历则返回true |
E next() | 返回集合中的下一个元素 |
default void remove() | 删除集合中上一次next方法返回的元素 |
代码示例
import java.util.*;
public class CollectionDemo {
public static void print(Object o) {
System.out.println(o);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList al2 = new ArrayList();
al2.add("Hello");
al2.add("World");
al2.add("Java");
// 1、使用foreach遍历集合
for (Object obj : al2)
print(obj);
print("");
// 2、使用while遍历集合
Iterator it = al2.iterator();
while (it.hasNext()) {
String al2Value = (String)it.next();
print(al2Value);
if (al2Value.equals("World")) {
it.remove();
}
}
print("");
// 3、使用for遍历集合
for (Iterator it1 = al2.iterator(); it1.hasNext();)
print(it1.next());
}
}
运行结果
Hello
World
Java
Hello
World
Java
Hello
Java
说明:就遍历来讲,本代码中使用了三种方法实现集合的遍历。for循环的遍历所占据的内存随着for循环的结束而消亡,但是while产生的部分内存不会。foreach循环的遍历并不属于Iterator的遍历,我们应该知道其更加简洁,该遍历下的集合元素不可改变!(也不能删除元素)
综上所述,Iterator中存在的方法使得集合在遍历时是没法被修改的(只能删除,不能增加、更改。当然,不推荐在迭代中删除元素),否则将会引发异常(ConcurrentModificationException)。另外,Iterator迭代器采用的是快速失败机制,一旦在迭代中检测到集合被修改就立即引发异常,这样可以避免(多线程)资源共享引发潜在问题。
Set集合
首先我们看以下代码
import java.util.*;
public class CollectionDemo {
public static void print(Object o) {
System.out.println(o);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Set hs = new HashSet();
hs.add(new String("Hello"));
boolean hsResult = hs.add(new String("Hello"));
print(hsResult);
print(hs);
}
}
输出结果
false
[Hello]
分析:我们已经知道Set集合中无法存储相同的元素。这是因为Set本身是无序的,我们每次查找元素时,都是直接查找元素内容本身,如果元素存在相同的,那就没法查找了。我们从上方代码中可以知道:
- Set是通过
equals
来区分元素是否相同的,因为此处创建了两个String对象,两者只有值相同,其他不同 - 存放相同元素时,add会返回
false
并存放失败
所以,Set也没比它的父接口Collection多什么东西,只是限制了元素不能相同。
HashSet
看如下代码
import java.util.*;
public class CollectionDemo {
public static void print(Object o) {
System.out.println(o);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
HashSet hs = new HashSet();
String str1 = new String("Hello1");
String str2 = new String("Hello2");
String str3 = new String("Hello3");
String str4 = new String("Hello4");
hs.add(str1);
hs.add(str2);
hs.add(str3);
hs.add(str4);
hs.add(null);
print(hs);
}
}
输出结果:
[null, Hello1, Hello4, Hello2, Hello3]
分析:从此代码中,我们能够得出:
- HashSet不能够保存元素的排列顺序,它每次的输出顺序都不相同,因为它是根据
hash
(哈希,散列)排布的 - 集合的元素可以是
null
- 还有一点:HashSet不是同步的,当我们使用多线程操控HashSet时,需要通过代码维持同步(此处代码没法体现)
另外,我们应该知道在Java中两个对象的hashCode
值相同时,其equals
值不一定是true
;但其equals
值为true
时,其hashCode
一定相等。
因为hash是通过计算对象地址转换成的int值,这个int值面对庞大的数据迟早会相同
hash的价值在于它的速度,当我们想访问HashSet的某个元素时,HashSet会通过hashCode直接计算出元素的地址并提取,此时hashCode起到了”索引”的功能(但我们知道Set是无序的,没有索引)。这和数组的工作非常相似,我们提供索引时,数组能够迅速找到该元素的值。但数组和HashSet相比的缺陷是:元素位置连续,数组长度不可变长。
另外,如果我们想让HashSet能够按照我们想要的方式排序,可以使用LinkedHashSet,它是HashSet的子类,它使用链表来维护元素的次序,维护链表需要损失一部分性能。
TreeSet
TreeSet类实现了SortedSet接口,它能够确保集合元素处于排序状态。因为此集合是有序的,因此其方法必然存在访问指定位置(或指定范围)的元素或集合。下面介绍一些该类特有常见的方法:
Modifier and Type | Method and Description |
---|---|
Comparator comparator() | 返回TreeSet采用的定制排序所使用的Comparator,没有就返回null |
E first() | 返回集合的第一个元素 |
E last() | 返回集合的最后一个元素 |
E lower(E e) | 返回小于指定元素的最大元素 |
E higher(E e) | 返回大于指定元素的最小元素 |
NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) | 返回Set的指定子集合,范围是[start,end) |
SortedSet headSet(E toElement) | 返回小于指定元素的其他元素组成的子集 |
NavigableSet tailSet(E fromElement, boolean inclusive) | 返回大于等于指定元素的其他元素组成的子集 |
代码示例
import java.util.*;
public class CollectionDemo {
public static void print(Object o) {
System.out.println(o);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(5);
ts.add(15);
ts.add(12);
ts.add(25);
print(ts);
print(ts.comparator());
print(ts.first());
print(ts.last());
print(ts.lower(14));
print(ts.higher(18));
print(ts.subSet(10, 18));
print(ts.headSet(14));
print(ts.tailSet(14));
}
}
输出结果
[5, 12, 15, 25]
null
5
25
12
25
[12, 15]
[5, 12]
[15, 25]
分析:从代码中可以看出,排序结果是由大小决定的,而不是插入的顺序。
因为TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后按元素升序排列,这就是TreeSet的自然排序。
自然排序
Java提供了一个Comparable接口,该接口定义了int compareTo(Object obj)方法,Java中很多常见的类也已经实现了Comparable接口。比如BigDecimal、BigInteger、Character、Boolean、String、Date、Time(它们都是比较其数值大小、字符的UNICODE值、true>false、时间大小来排序的)。
- 如果我们想将一个以上的对象添加到TreeSet时,该对象必须已经实现了Comparable接口,否则比较时会抛出异常
- 当我们添加对象时,程序会通过红黑树确定其存储位置,因此相同对象(compareTo()返回值为0)无法同时保存
- 添加对象时,程序会自动与已存在的对象比较大小,因此应该尽量保证TreeSet中元素类型相同
- 如果添加了可变元素,那么它将变得不可删除,所以请尽量添加常量元素
定制排序
我们已经知道obj1.compareTo(obj2)方法返回0时代表两个元素值相同,负数代表obj1\
import java.util.*;
class M {
int age;
public M(int age) {
this.age = age;
}
public String toString() {
return "M[age:" + age + "]";
}
}
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new Comparator() {
public int compare(Object obj1, Object obj2) {
M m1 = (M)obj1;
M m2 = (M)obj2;
return m1.age > m2.age ? 1 : m1.age < m2.age ? -1 : 0;
}
});
ts.add(new M(20));
ts.add(new M(-6));
ts.add(new M(0));
System.out.println(ts);
}
}
运行结果
[M[age:-6], M[age:0], M[age:20]]
分析:由代码可以看出我们在创建TreeSet集合对象的时候同时创建了一个匿名内部类对象,该对象负责ts对象的排序,但是仍然不能向集合中添加不同类型的元素!
List集合
在Collection
的介绍中,我使用的是ArrayList
,它是List的一个典型的子接口,除了上面讲到的内部方法外,本节我还会使用它来记录List的特有方法。
List中特有的部分方法
Modifier and Type | 功能 |
---|---|
void add(int index, E element) | 在列表的指定位置插入指定元素(可选操作)。 |
boolean addAll(int index, Collection c) | 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 |
E get(int index) | 返回列表中指定位置的元素。 |
int indexOf(Object o) | 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
int lastIndexOf(Object o) | 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 |
boolean remove(Object o) | 从此列表中移除第一次出现的指定元素(如果存在)(可选操作)。 |
E set(int index, E element) | 用指定元素替换列表中指定位置的元素(可选操作)。 |
List subList(int fromIndex, int toIndex) | 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
代码示例
import java.util.*;
public class ListDemo {
public static void print(Object obj) {
System.out.println(obj);
}
public static void main(String[] args) {
List al = new ArrayList();
List al1 = new ArrayList();
//一般添加元素到集合中
al.add("Hello");
al.add("world");
al.add("java");
al.add("Hello");
al1.add("Hello");
al1.add("world");
al1.add("java");
print(al);
// 1、添加元素到指定索引
al.add(1, "Test");
print(al);
// 2、移除指定索引的元素
al.remove(1);
print(al);
// 3、添加集合到指定索引处
al.add(1, al1);
print(al);
// 4、返回指定索引处的元素
print(al.get(2));
// 5、返回指定元素第一次出现的位置
print(al.indexOf(new String("Hello")));
// 6、返回指定元素最后一次出现的位置
print(al.lastIndexOf("Hello"));
// 7、更改指定索引处的元素为指定元素
al.set(4, "Test");
print(al);
// 8、获取列表的子列表
print(al.subList(1, 3));
}
}
输出结果
[Hello, world, java, Hello]
[Hello, Test, world, java, Hello]
[Hello, world, java, Hello]
[Hello, [Hello, world, java], world, java, Hello]
world
0
4
[Hello, [Hello, world, java], world, java, Test]
[[Hello, world, java], world]
分析:从代码中我们可以看出,
- List的方法一般是以其索引来操控指定元素的
- 集合加入指定索引处后是以整个集合当做一个元素来处理的
- 代码36行,新建了一个
String对象
“Hello”能够找到原集合中指定元素的位置,说明他是通过equals()
来判断元素是同一个元素的
遍历访问List元素与迭代
在Collection中已经通过三种方式来遍历集合元素,但第三种与第二种遍历其实是相同的。真正的for循环遍历应该是控制它的索引来完成的:
import java.util.*;
public class ListDemo {
public static void print(Object obj) {
System.out.println(obj);
}
public static void main(String[] args) {
List al = new ArrayList();
List al1 = new ArrayList();
al.add("Hello");
al.add("world");
al.add("java");
for (int i = 0; i < al.size() ; i++ ) {
print(al.get(i));
}
}
}
输出结果
Hello
world
java
另外,我们已经知道集合在使用Iterator接口遍历元素时不能更改元素(除了remove())。因此List接口提供了Iterator的子接口ListIterator接口来增加遍历的功能!
代码示例
import java.util.*;
public class ListDemo {
public static void print(Object obj) {
System.out.println(obj);
}
public static void main(String[] args) {
List al = new ArrayList();
List al1 = new ArrayList();
al.add("Hello");
al.add("world");
al.add("java");
ListIterator lit = al.listIterator();
while (lit.hasNext()) {
Object obj = lit.next();
print(obj);
if (obj == "java") {
lit.add("listIterator");
}
if (obj == "world") {
al.set(0, "test");
}
}
print(al);
print("-------分割符---------");
while (lit.hasPrevious()) {
Object obj = lit.previous();
print(obj);
}
}
}
输出结果
Hello
world
java
[test, world, java, listIterator]
-------分割符---------
listIterator
java
world
test
说明 :从代码中我们可以看出
- ListIterator能够让集合在遍历时添加、删除或者更改元素
- ListIterator添加了与next()对应的previous()方法,与hasNext()对应的hasPrevious()方法,来反向遍历!
ArrayList
前面一直使用ArrayList来展示集合,因此大部分功能已经讲完。来说一下它本身。
ArrayList与Vector都是基于数组实现的List类,他们的实例化对象使用initialCapacity参数来设置数组的长度(默认长度为10),当长度超过后就会自动增加长度(ArrayList增加50%,Vector增加100%)。另外,我们也可以使用ensureCapacity(int minCapacity)来手动一次性增加长度,还可以使用trimToSize()调整去除两个集合的多余空间。
LinkedList
LinkedList是一个List集合,因此它可以通过索引来访问,同时它也实现了Deque接口,因此它可以当做双端队列使用。因此一般对LinkedList的操作方法类似于对于堆栈的操作!
代码示例
import java.util.*;
public class LinkedListDemo {
public static void print(Object obj) {
System.out.println(obj);
}
public static void main(String[] args) {
LinkedList lld = new LinkedList();
lld.add("Hello");
lld.add("world");
lld.add("java");
print(lld);
// 1、将元素元素加入队列的尾部
lld.offer("test1");
print(lld);
// 2、将元素元素加入队列的顶部
lld.push("lld");
print(lld);
// 3、将元素元素加入队列的顶部
lld.offerFirst("test2");
print(lld);
// 4、弹出队列顶部元素(删除)
print(lld.pop());
print(lld);
// 5、访问但不删除队列顶部的元素
print(lld.peekFirst());
print(lld);
// 6、访问但不删除队列底部的元素
print(lld.peekLast());
print(lld);
// 7、删除并访问队列最后一个元素
print(lld.pollLast());
print(lld);
}
}
输出结果
[Hello, world, java]
[Hello, world, java, test1]
[lld, Hello, world, java, test1]
[test2, lld, Hello, world, java, test1]
test2
[lld, Hello, world, java, test1]
lld
[lld, Hello, world, java, test1]
test1
[lld, Hello, world, java, test1]
test1
[lld, Hello, world, java]
Map集合
简单介绍
因为Map集合保存的是具有映射关系的数据,因此它可以类比成一个函数系统f(x),函数系统中保存着两组数值,一组是变量(Map里面的key),一组是函数值(Map里面的value),我们知道在函数系统中变量x是不可相同的,而函数值f(x)可以相同,Map也是如此。
Map的key集存储形式和Set接口完全相同,不可重复,没有顺序。Map中存在一个keySet()方法返回Map里所有key值组成的Set集。
Map的value非常类似于List:元素与元素之间可以重复,元素根据索引来查找,只是索引由一个整数改为对象。
代码示例
import java.util.*;
public class HashMapDemo {
public static void print(Object obj) {
System.out.println(obj);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
HashMap hm = new HashMap<>();
HashMap hm1 = new HashMap<>();
// 1、向Map中添加映射关系
hm.put(0, "hello");
hm.put(11, "world");
hm.put(2, "java");
hm.put("test", "java");
hm.put(5, null);
print(hm);
hm1.put(2, "test");
hm1.put(null, "hashmap");
// 2、查询Map中是否包含指定的key值
print(hm.containsKey(11));
// 3、查询Map中是否包含指定的value值
print(hm.containsValue("hello"));
// 4、返回Map中包含key-value对所组成的Set集合
Set s1 = hm.entrySet();
print(s1);
// 5、返回对应key值对应的value,如果没有该key,返回null
print(hm.get(2));
// 6、查询Map是否为空
print(hm.isEmpty());
// 7、返回所有key值组成的Set集合
print(hm.keySet());
// 8、将指定Map中的键值对添加到本Map中
hm.putAll(hm1);
print(hm);
// 9、移除指定key值的键值对
hm.remove(11);
print(hm);
// 10、返回Map的元素个数
print(hm.size());
// 11、清除Map中所有键值对
hm1.clear();
print(hm1);
// 12、返回Map中所有values组成的Collection
print(hm.values());
}
}
运行结果
{0=hello, 2=java, test=java, 5=null, 11=world}
true
true
[0=hello, 2=java, test=java, 5=null, 11=world]
java
false
[0, 2, test, 5, 11]
{0=hello, null=hashmap, 2=test, test=java, 5=null, 11=world}
{0=hello, null=hashmap, 2=test, test=java, 5=null}
5
{}
[hello, hashmap, test, java, null]
说明 :
- key或者value都可以是null
- 如果没有泛型规范,key值可以是任意值
- Map提供一个Entry内部类来封装key-value对,计算Entry时只需要考虑它封装的key值就行
- HashMap重写的toString()方法
- 3中通过equals来判定是否包含指定value值
遍历访问Map
我们可以通过key值当做索引访问每一个键值对
for (Object o : hm.keySet()) {
print(o + "=" + hm.get(o));
}
for (Iterator it = hm.keySet().iterator(); it.hasNext();) {
Object o = it.next();
print(o + "=" + hm.get(o));
}
HashMap与Hashtable
两者都是Map的典型实现类,两者之间的关系可以类比于ArrayList与Vector的关系:
- Hashtable很古老,从JDK1.0就存在了,它包括了两个操控elements的古老方法
- Hashtable是一个线程安全的Map实现,且不允许使用null作为key和value
- HashMap线程不安全,但其效率更高一点
我们应该尽量不使用Hashtable。
如果我们使用自定义类作为key值,那我们重写该类的equals与hashCode时应该保证其判断标准一致:equals输出为true时hashCode也是一样的。
LinkedHashMap实现类就像是Set中的LinkedHashSet一样可以记住元素的存放顺序,其他属性可以类比LinkedHashSet。
SortedMap接口与TreeMap实现类
正如Sort接口->SortedSet子接口->TreeSet实现类,Map->SortedMap子接口->TreeMap实现类。
TreeMap通过红黑树数据结构根据key对节点进行排序,保证所有的key-value处于有序状态。TreeMap有两种排序方式
- 自然排序:所有的key都必须实现了Comparator接口,而且所有key应该是同一个类的对象,否则会抛出ClassCastException异常!
- 定制排序:创建TreeMap时传入一个Comparator对象,该对象负责对TreeMap进行key排序,此时不要求Map的key值实现了Comparator接口。
- 使用自定义的类当做key时必须保证equals(true/false)返回的值与compareTo(0/非0)相同
根据key值访问TreeMap的键值对:
代码实例
import java.util.*;
public class TreeMapDemo {
public static void print(Object obj) {
System.out.println(obj);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
TreeMap tm = new TreeMap();
tm.put(1, "hello");
tm.put(21, "java");
tm.put(5, "world");
tm.put(2, "test");
// 1、返回该Map中最小key对应的key-value对,如果为空,返回null
print(tm.firstEntry());
// 2、返回Map中最小key值
print(tm.firstKey());
// 3、返回该Map中最大key对应的key-value对,如果为空,返回null
print(tm.lastEntry());
// 4、返回Map中最大key值
print(tm.lastKey());
// 5、返回大于指定key值的最小key的值
print(tm.higherKey(2));
// 6、返回大于指定key值的最小key值所在的键值对
print(tm.higherEntry(2));
// 7、返回小于指定key值的最大key的值
print(tm.lowerKey(5));
// 8、返回小于指定key值的最大key值所在的键值对
print(tm.lowerEntry(5));
// 9、返回Map的子Map,默认左闭右开
print(tm.subMap(2, 21));
// 10、返回Map的子Map,可以指定左右是否闭
print(tm.subMap(2, true, 21, true));
// 11、返回大于(包括)指定key值的所有key值所在键值对组成的子Map
print(tm.tailMap(2));
// 12、返回大于(是否包括取决于第二个参数)指定key值的所有key值所在键值对组成的子Map
print(tm.tailMap(2, false));
// 13、返回小于(不包括包括)指定key值的所有key值所在键值对组成的子Map
print(tm.headMap(5));
// 14、返回小于(是否包括取决于第二个参数)指定key值的所有key值所在键值对组成的子Map
print(tm.headMap(5, true));
}
}
运行结果
1=hello
1
21=java
21
5
5=world
2
2=test
{2=test, 5=world}
{2=test, 5=world, 21=java}
{2=test, 5=world, 21=java}
{5=world, 21=java}
{1=hello, 2=test}
{1=hello, 2=test, 5=world}
分析 :这和TreeSet的操作真的很像!
工具类Collections
Collections是Java中提供了操作Set、List、Map等集合的工具类,我们可以使用它来对集合元素进行排序、查询、修改等。
针对List集合的用法:
代码实例
import java.util.*;
public class ListTest {
public static void print(Object obj) {
System.out.println(obj);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(-2);
al.add(0);
al.add(2);
al.add(5);
al.add(10);
al.add(-6);
print("原排序" + al);
// 1、翻转集合元素次序
Collections.reverse(al);
print("翻转次序:" + al);
// 2、将list集合元素按照随机排序排序
Collections.shuffle(al);
print("随机排序:" + al);
// 3、将list集合元素按自然排序
Collections.sort(al);
print("自然排序:" + al);
// 4、将指定 的两个位置的元素相互交换
Collections.swap(al, 1, 4);
print("交换1,4位置:" + al);
//5、移动后面指定个数的元素到集合前面(+),移动前面指定个数的元素到集合后面(-),
Collections.rotate(al, 2);
print("移后到前2:" + al);
Collections.rotate(al, -3);
print("移前到后3:" + al);
}
}
运行结果:
原排序[-2, 0, 2, 5, 10, -6]
翻转次序:[-6, 10, 5, 2, 0, -2]
随机排序:[-2, 10, 2, -6, 5, 0]
自然排序:[-6, -2, 0, 2, 5, 10]
交换1,4位置:[-6, 5, 0, 2, -2, 10]
移后到前2:[-2, 10, -6, 5, 0, 2]
移前到后3:[5, 0, 2, -2, 10, -6]
说明:sort还有一个方法static void sort(List list,Comparator c)
根据指定的Comparator产生顺序对list集合元素排序。
除了上面的一些操作,Collections还提供了一些查找、替换集合元素的常见方法
代码实例
import java.util.*;
public class CollectionsDemo {
public static void print(Object obj) {
System.out.println(obj);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList al = new ArrayList();
ArrayList al1 = new ArrayList();
al.add(-2);
al.add(-3);
al.add(0);
al.add(12);
al.add(-3);
al1.add(12);
al1.add(-3);
print("原集合:" + al);
// 1、查找指定元素的索引位置(只适用于List列表)
print("列表中0所在的索引:" + Collections.binarySearch(al, 0));
// 2、第一次出现的位置
print(Collections.indexOfSubList(al, al1));
// 3、最后一次出现的位置
print(Collections.lastIndexOfSubList(al, al1));
// 4、查找集合中最大的元素
print("集合中的最大元素:" + Collections.max(al));
// 5、查找集合中最小的元素
print("集合中的最小元素:" + Collections.min(al));
// 6、返回指定元素出现的次数
print("元素-3出现的次数:" + Collections.frequency(al, -3));
// 7、使用新值替换旧值
print("用-1替换-3:" + Collections.replaceAll(al, -3, -1));
// 8、使用指定元素替换List所有元素
Collections.fill(al, "hello");
print("使用hello替换list所有元素:" + al);
}
}
运行结果
原集合:[-2, -3, 0, 12, -3]
列表中0所在的索引:2
3
3
集合中的最大元素:12
集合中的最小元素:-3
元素-3出现的次数:2
用-1替换-3:true
使用hello替换list所有元素:[hello, hello, hello, hello, hello]
同步控制
因为集合框架中的实现类HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap、TreeMap都是线程不安全的,所以Java在Collections中引入了同步控制来保证线程安全。
代码实例
import java.util.*;
public class SynchronizedTest {
public static void main(String[] args) {
Collection c = Collections.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set s = Collections.synchronizedSet(new HashSet());
Map m = Collections.synchronizedMap(new HashMap());
}
}
说明 :以上代码将会创建对应的线层同步的集合
设置不可变集合
我们一般使用的集合都是可以随时添加元素的,这在某些场合不太安全,因此创建一些不可变的集合至关重要。Collections中提供了三种方法来解决这一问题。
- emptyXxx():返回一个空的、不可变的集合对象
- singletonXxx():返回一个只有包含指定对象(一个元素)的、不可变的对象
- unmodifiableXxx():返回指定集合对象的不可变视图
这三个方法都对List、Set和Map有效。
代码实例
import java.util.*;
public class UnmodifiableTest {
public static void main(String[] args) {
List unmodifiableList = Collections.emptyList();
Set unmodifiableSet = Collections.singleton("hello");
Map hp = new HashMap();
hp.put(1, "hello");
hp.put(2, "java");
Map unmodifiableMap = Collections.unmodifiableMap(hp);
unmodifiableList.add("world");
unmodifiableSet.add("world");
unmodifiableMap.put(3, "test");
}
}
运行结果
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at UnmodifiableTest.main(UnmodifiableTest.java:10)
说明
我们在编译时不会出现错误,但是运行时将会出现以上错误,这是因为三个集合都是不可变的,如果我们试图向其中加入其他元素,将会报错!