JDk中主要集合类型主要分为以下四种;
集合相关接口和类:
Collection是JDK中集合类型上层接口,很多相关接口和集合类都派生自它。
对象集合的一个限制:Java集合不能保存原始数据类型的数据,传入原始数据类型将转换为包装类
集合主要操作:
遍历操作:
1.使用迭代器对象(Iterator)
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class TestMain {
public static void main(String[] args) {
Collection books = new HashSet();
books.add("One book");
books.add("Two book");
books.add("Three book");
//获取books集合对应的迭代器
Iterator it = books.iterator();
while(it.hasNext())
{
String book = (String)it.next();
System.out.println(book);
}
System.out.println("集合中的元素为:"+books);
}
}
Iterator接口定义了三个方法:
public Iterator<E> {
boolean hasNext(); // return true if the iteration has more elements
E next(); // return the next element in the iteration
void remove(); // remove the last element returned by the iterator
}
所有实现了Collection接口的集合对象,都提供了一个iterator()方法,因此可以像上述方式遍历
如果非要在遍历时非要删除集合中的元素,则必须通过迭代器对象的remove方法,而不能通过集合 对象直接删除
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class TestMain {
public static void main(String[] args) {
//创建一个集合
Collection books = new HashSet();
books.add("One book");
books.add("Two book");
books.add("Three book");
//获取books集合对应的迭代器
Iterator it = books.iterator();
while(it.hasNext())
{
String book = (String)it.next();
System.out.println(book);
if (book.equals("Three book"))
{
//使用Iterator迭代过程中,通过它来移除集合元素是安全的
it.remove();//是it.remove()不是books.remove()
}
}
System.out.println("移除元素之后:"+books);
}
}
2.foreach:
这个比较简单,但要注意当使用foreach循环遍历集合时,注意不要再向集合中追加或删除元素,否则,会引发 ConcurrentModificationException
List可以看成是一种动态改变大小的数组,接口方法如下:
注意,List只是一个接口,并不是类!!!
List为变量名,其真实对象需要是实现该接口的子类如ArrayList
ArrayList:可以看成一个动态数组
除了ArrayList,JDK中还有另一个类Vector同样实现了List接口,两者功能基本一样,但前者不是线程安全的。
下面实例以List接口变量名ArrayList为真实对象:
import java.util.ArrayList;
import java.util.List;
public class TestMain {
public static void main(String[] args) {
List books = new ArrayList();
//向books集合中添加三个元素
books.add(new String("One book"));
books.add(new String("Two book"));
books.add(new String("Three book"));
System.out.println(books);
//将新字符串对象插入在第二个位置
books.add(1 , new String("new book"));
for (int i = 0 ; i < books.size() ; i++ )
{
System.out.println(books.get(i));
}
//删除第三个元素
books.remove(2);
System.out.println(books);
//判断指定元素在List集合中位置:输出1,表明位于第二位
System.out.println(books.indexOf(new String("new book")));
//将第二个元素替换成新的字符串对象
books.set(1, new String("new book2"));
System.out.println(books);
//将books集合的第二个元素(包括)到第三个元素(不包括)截取子集合
System.out.println(books.subList(1 , 2));
}
}
List中的indexOf(Object o)和lastIndexOf(Object o)方法:传入的类需要实现equal方法,即需要能够判断两个对象的内容是否完全一致。
固定大小List:定义于Arrays类的内部类ArrayList,是一个固定长度的List集合,只能遍历访问,不能增加和删除元素,否则,会引发 UnsupportedOperationException。
Stack:JDK中提供了现成的堆栈类——Stack,注意它派生自Vector,所以它是线程安全的。
import java.util.Stack;
public class TestMain {
public static void main(String[] args) {
Stack names = new Stack<>();
names.push("Raymond");
names.push("David");
System.out.println("Top of stack: " + names.peek());
names.pop();
System.out.println("Top of stack: " + names.peek());
names.push("Cynthia");
System.out.println("Top of stack: " + names.peek());
//如果堆栈为空,再次pop会抛出EmptyStackException
if (!names.empty()) {
names.pop();
}
System.out.println("Top of stack: " + names.peek());
names.pop();
if (!names.empty()) {
System.out.println("Top of stack: " + names.peek());
} else {
System.out.println("Stack empty.");
}
}
}
Vector更多参考:https://www.cnblogs.com/skywang12345/p/3308833.html
LinkedList更多参考:https://www.cnblogs.com/skywang12345/p/3308807.html
Queue接口代表的是“经典”的队列,JDK中还扩充定义了另一个Deque接口,代表“双向队列”,用途更广。
PriorityQueue:非线程安全,无容量限制。允许元素重复。其顺序由元素自己(如果实现了Comparable接口)或 Compartor提供,会将它所包容的元素自动排序。
import java.util.PriorityQueue;
public class TestMain {
public static void main(String[] args) {
PriorityQueue pq = new PriorityQueue();
//下面代码依次向pq中加入四个元素
pq.offer(6);
pq.offer(-3);
pq.offer(9);
pq.offer(0);
pq.offer(110);
//输出pq队列,并不是按元素的加入顺序排列,而是按元素的二叉树顺序排列
System.out.println(pq);
//访问队列第一个元素,其实就是队列中最小的元素:-3
System.out.println(pq.peek());
pq.poll();
System.out.println("\n 移除队列中的head元素之后");
System.out.println(pq);
pq.remove(9);
System.out.println("\n 移除队列中的元素9之后");
System.out.println(pq);
}
}
CurrentLinkedQueue:这是一个线程安全的(FIFO:First In First Out)的容量不限的队列。其内部使用一种基于 “CAS(Compare and Swap)”的算法,能保证访问它的线程总能完 成它要执行的操作而不会被提前中断
BlockingQueue接口:此接口派生自Queue,最适合于实现生产者-消费者问题
Deque接口:双向队列。既像Queue,又像Stack。
Map接口成员:
HashMap:
HashMap简单使用(遍历):
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class TestMain {
public static void main(String[] args) {
HashMap hm = new HashMap<>();
for (int i = 1; i < 10; i++) {
hm.put("Key" + i, i);
}
// 使用KeySet遍历集合
Set keySet = hm.keySet();
for (String key : keySet) {
System.out.println(key + ":" + hm.get(key));
// 循环遍历过程中,不允许移除元素,否则,引发ConcurrentModificationException
}
System.out.println("\n使用Iterator遍历并移除元素");
Set
HashMap可以使用null作为key,且只会有一个null作key的键值对,多的会覆盖掉。
HashTable:HashMap和HashTable两者功能基本一样,但HashTable是线程安全的
Map集合对Key要求:
上述特性对HashTable的containsValue()方法返回值的影响如实例:
import java.util.*;
public class TestMain {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
ht.put(new MyKey(60000) , "key值为60000所对应的Value");
ht.put(new MyKey(87563) , "Key值为87563所对应的Value");
ht.put(new MyKey(1232) , new B());
System.out.println(ht);
//只要两个对象通过equals比较返回true,Hashtable就认为它们是相等的value。
//因为Hashtable中有一个B对象,它与任何对象通过equals比较都相等,所以下面输出true。
System.out.println(ht.containsValue("测试字符串"));
//只要两个A对象的count属性相等,它们通过equals比较返回true,且hashCode相等
//Hashtable即认为它们是相同的key,所以下面输出true。
System.out.println(ht.containsKey(new MyKey(87563)));
//下面语句可以删除最后一个key-value对
ht.remove(new MyKey(1232));
for (Object key : ht.keySet())
{
System.out.print(key + "---->");
System.out.print(ht.get(key) + "\n");
}
}
}
class MyKey
{
int value;
public MyKey(int count)
{
this.value = count;
}
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (obj != null && obj.getClass() == MyKey.class)
{
MyKey a = (MyKey)obj;
if (this.value == a.value)
{
return true;
}
}
return false;
}
public int hashCode()
{
return this.value;
}
public String toString(){
return "MyKey:"+this.value;
}
}
class B
{
public boolean equals(Object obj)
{
return true;
}
}
Properties类:派生自HashTable,可以方便地处理“属性-值”对,并且可以很方便地将其保存到文件中
NavigableMap接口:,保证在遍历集合时,一定是按照升序进行的
实现NavigableMap接口最常用的类型是 TreeMap,它的用法与标准的Map没什么两样
TreeMap的独特之处在于:它内部基于红黑树 (red-black tree)对Key进行排序。因此,它的元素是“有序”的。
import java.util.*;
public class TestMain {
public static void main(String[] args) {
TreeMap tm = new TreeMap();
tm.put(new R(3) , "Key值为3的value");
tm.put(new R(-5) , "Key值为-5的value");
tm.put(new R(9) , "Key值为9的value");
System.out.println("TreeMap集合中的所有元素:\n"+tm);
//返回该TreeMap的第一个Entry对象
System.out.println("\n第一个Entry对象:"+tm.firstEntry());
//返回该TreeMap的最后一个key值
System.out.println("最后一个key值:"+tm.lastKey());
//返回该TreeMap的比new R(2)大的最小key值。
System.out.println("比new R(2)大的最小key值:"+tm.higherKey(new R(2)));
//返回该TreeMap的比new R(2)小的最大的key-value对。
System.out.println("比new R(2)小的最大的key-value对:"+tm.lowerEntry(new R(2)));
//返回该TreeMap的子TreeMap
System.out.println("该TreeMap的子TreeMap:"+tm.subMap(new R(-1) , new R(4)));
}
}
//R类,重写了equals方法,如果value属性相等返回true
//重写了compareTo(Object obj)方法,如果value属性相等返回0;
class R implements Comparable
{
int value;
public R(int value)
{
this.value = value;
}
public String toString()
{
return "R(value属性:" + value + ")";
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null && obj.getClass() == R.class)
{
R r = (R)obj;
if (r.value == this.value)
{
return true;
}
}
return false;
}
public int compareTo(Object obj)
{
R r = (R)obj;
if (this.value > r.value)
{
return 1;
}
else if (this.value == r.value)
{
return 0;
}
else
{
return -1;
}
}
}
JDK中提供了以下Map类型实现多线程环境下的安全访问:
ConcurrentHashMap
ConcurrentNavigableMap
ConcurrentSkipListMap
Set也是一种Collection,其中不允许有重复元素。Set通过调用对象的equals方法确定两个对象是否相同。
HashSet原理:
import java.util.*;
public class TestMain {
public static void main(String[] args) {
HashSet books = new HashSet();
//分别向books集合中添加2个A对象,2个B对象,2个C对象
books.add(new A());
books.add(new A());//hash值不同,故可以添加,位于不同槽
books.add(new B());
books.add(new B());//hash值相同,但equal返回false,可添加位于同一个槽
books.add(new C());
books.add(new C());//hash值相同,equal返回true,故不可再添加
//输出:[B@1, B@1, C@2, A@15db9742, A@6d06d69c]
System.out.println(books);
}
}
//类A的equals方法总是返回true,但没有重写其hashCode()方法
class A
{
public boolean equals(Object obj)
{
return true;
}
}
//类B的hashCode()方法总是返回1,但没有重写其equals()方法
class B
{
public int hashCode()
{
return 1;
}
}
//类C的hashCode()方法总是返回2
class C
{
public int hashCode()
{
return 2;
}
public boolean equals(Object obj)
{
return true;
}
}
使用HashSet集合(TreeSet类似)保存对象时要注意,尽量不要修改对象的属性值,否则,可能会出现很奇怪的现象。推荐在HastSet中保存不可变类的实例。
LinkedHashSet:派生自HashSet,也是按照对象的hashCode来保存,但它同时使用双向链表来维护对象的顺序,当遍历LinkedHashSet时,将按插入顺序来显示集合中的对象
SortedSet接口:SortedSet保证总是按照升序方式访问集合中的数据。使用SortedSet的一个最大好处是:如果合并两个这样的集合,合并之后,集合仍然是有序的。JDK中的TreeSet实现了SortedSet接口。
TreeSet:
import java.util.*;
public class TestMain {
public static void main(String[] args) {
TreeSet nums = new TreeSet();
//向TreeSet中添加四个Integer对象
nums.add(5);
nums.add(2);
nums.add(10);
nums.add(-9);
//输出集合元素,看到集合元素已经处于排序状态
System.out.println("集合元素:"+nums);
//输出集合里的第一个元素
System.out.println("集合里的第一个元素:"+nums.first());
//输出集合里的最后一个元素
System.out.println("集合里的最后一个元素:"+nums.last());
//可以输出指定元素的“前一个”和“后一个”
System.out.println("10之前的那个元素是:"+nums.lower(10));
System.out.println("10之后的那个元素是:"+nums.higher(10));
//返回小于4的子集,不包含4
System.out.println("返回小于4的子集,不包含4:"+nums.headSet(4));
//返回大于5的子集,如果Set中包含5,子集中还包含5
System.out.println("返回大于5的子集:"+nums.tailSet(5));
//返回大于等于-3,小于4的子集。
System.out.println("返回大于等于-3,小于4的子集:"+nums.subSet(-3 , 4));
}
}
参考:
金旭亮Java编程系列(大部分内容摘自该教程)