Collection集合
一.Java集合API
Java Collection Framework API
—— 集合,也叫容器(集合的功能相当于容器),Java的集合(容器)是用来“装对象”的。
二.Set集合类(接口)
—— Set集合几乎等同于 Collection 集合,二者行为完全相似。
1.遍历Set集合
① 用迭代器(Iterator<E>)
② 用foreach循环
遍历Set集合及部分Collection/Set集合常用方法示例代码:
import java.util.*;
public class CollectionTest
{
public static void main(String[] args)
{
//加泛型限制的集合,只能装入<>指定内指定类型的对象
//Collection<String> c1 = new HashSet<String>();
//这里不能用new Collection来创建对象,应为Collection是集合(接口),而使用
//new HashSet创建对象,HashSet类实现了Collection集合(接口).
Collection<String> c1 = new HashSet<>(); //棱形语法( Since JDK1.7)
//调用add()方法,装入对象
c1.add("刘亦菲");
c1.add("Crystal");
c1.add("Destiny");
//调用contains()方法,判断是否包含所指定的对象
System.out.println(c1.contains("liuyifei"));
System.out.println(c1.contains("Crystal"));
// foreach遍历集合c1
for(String ele : c1)
{
System.out.println(ele);
}
//Iterator迭代器遍历c1
//(注意:这里因为c2的定义在迭代器之后,所以不能遍历c2)
Iterator<String> it = c1.iterator();
//调用Iterator的hasNext()、next()方法
while(it.hasNext())
{
System.out.println(it.next());
}
Collection <String> c2 = new HashSet<>();
c2.add("刘亦菲");
c2.add("crystal");
c2.add("destiny");
//调用remove()方法,相当于做集合 c2-c1
c2.removeAll(c1);
//调用isEmpty()方法,判断集合 c2-c1 是否为空
System.out.println(c2.isEmpty());
//输出集合 c2-c1
System.out.println(c2);
//调用addAll()方法,相当于做集合 c2+c1
c2.addAll(c1);
//输出集合c2+c1
System.out.println(c2);
//调用retainAll()方法,相当于做集合 c2 ∩ c1
c2.retainAll(c1);
//输出集合c2 ∩ c1
System.out.println(c2);
}
}
2.HashSet类:是Set集合类的实现类
① HashSet的存储机制
底层是用一个数组存储元素 ———— 这个数组的长度永远是2的n次方。
HashSet的构造器HashSet(int initialCapacity, float loadFactor)说明:
—— initialCapacity :控制底层数组长度,默认是16 ,如果传入的数组长度不是2的n次方,HashSet会自动将其扩展到2的n次方。
—— loadFactor :当HashSet “感觉到底层数组快满”时,将再次创建一个长度为原数组长度2倍的数组,并将原数组的元素复制到新数组中,原数组就成为了“垃圾”—— 专业术语才成为“rehash(重hash)”。
loadFactor 的字面翻译为 “负载系数”。可理解为的原数组的装填程度达到这个“负载系数”是,就判定数组已满。loadFactor 越小越耗内存,越大性能越低,当loadFactor越大,及数组越满时,才“重hash”,则越有可能出现链表。
② HashSet的 存 机制:
(1) 当添加元素时,HashSet会调用该对象的hashCode()方法,得到一个int 值;
(2) 根据hashCode()方法返回的int值,计算出其在底层数组的存储位置(数组中的索引);
(3) 如果要添加元素的位置为空,则直接添加;若不为空,则此处会形成链表。
③ HashSet的 取 机制:
(1) 当添加元素时,HashSet会调用该对象的hashCode()方法,得到一个int 值;
(2) 根据hashCode()方法返回的int值,计算出其在底层数组的存储位置(数组中的索引);
(3) 如果该位置恰好是要取出的元素,就直接取出;如果该位置有链表,,则需要依次搜索链表的元素。
最理想的情况下,HashSet的性能几乎可以匹敌数组,存取性能非常高。
④ HashSet认定对象相等的条件:
(1) 两个对象的 hashCode()方法返回值相等
(2) 两个对像通过 equals()方法比较返回true
—— 这就要求自定义类的hashCode()方法和equals()方法是一致的,要求程序重写equals()方法所用的关键属性,与计算hashCode()方法所用的关键属性相同。
3.TreeSet类:是Set集合类的子接口SortedSet的实现类
特征:保证Set里面的元素是“大小排序”的。
① TreeSet 的底层实现是用标准的“红黑树”存放数据,元素的存入、检索的性能也比较好。
—— 在HashSet没有大量出现“链表”的情况下,HashSet性能比TreeSet性能好,若是HashSet经常发生“重Hash”,一般选用TreeSet,TreeSet不会像HashSet有“空桶”,同时可以保证集合元素是按“大小”排序的。
② 使用TreeSet的要求:集合元素必须是可以比较大小的。
—— Java 比较大小的两种方式:
(1) 自然排序 —— 所有集合元素实现Comparable 接口。
集合元素实现了 Comparable 接口后,集合元素自身就是可以排 序的;
(2) 定制排序 —— 要求创建TreeSet时,提供一个Comparator 对象。
Comparator 对象可负责对元素进行比较大小,集合元素无需实现 Compareable 接口,因此集合元素本身是无法排序的。
③ TreeSet相等的判定:只要两个对象通过compareTo()比较返回 0,就表示相等。
示例程序:
import java.util.*;
public class TreeSetTest
{
//本程序演示 TreeSet 对元素的排序
public static void main(String[] args)
{
TreeSet<Integer> set=new TreeSet<>();
//这里的 Integer是对象类型,不能用 int 代替
//其它基本类型与此用法相同
//int型数据会自动装箱成 Integer 对象
set.add(20);
set.add(15);
set.add(48);
set.add(59);
set.add(20); //重复添加可以通过编译
//但是运行时只打印一次
System.out.println(set);
TreeSet<String> set2=new TreeSet<>();
set2.add("20");
set2.add("abf");
set2.add("bdg");
set2.add("A");
System.out.println(set2);
}
}
运行结果:
[15, 20, 48, 59]
[20, A, abf, bdg]
三.List 集合类(接口)
List 集合提供了大量的“根据索引”来存取元素的方法,由于List可以“根据索引”来存取元素,因此与Set 集合相比,多了一个 “索引遍历”的方法。
① ArrayList 与 Vector 的存储机制:
(1) 二者底层完全基于数组 —— 对元素的存储完全基于数组实现,因而性能非常快。
(2) 二者的区别:Vector是从JDK1.0就有的集合,从JDK1.2 后,Sun 公司重写了ArrayList ,替代了原来的 Vector。ArrayList 是线程不安全的,Vector 是线程安全的,但是 ArrayList 的性能比Vector 要好,在多线程环境下,可以使用Collections 保证ArrayList 为线程安全的。
② LinkedList :即使线性表,也是队列,还是栈。
LinkedList 底层是基于链表实现的,其性能类似于ArrayList。
LinkedList 与 ArrayList的比较:
ArrayList :由于可以根据底层数组的索引存取元素,所以性能非常快。但是当插入、删除元素是,插入或删除点后面的元素需要 整体后移 。
LinkedList : 由于底层采用了链表来存储元素,因此根据索引存取元素性能比较慢,但是当插入、删除元素时不需要进行整体后移,此时性能非常快。
示例程序:
import java.util.*;
public class ListTest
{
//本程序演示 List集合的基本方法的使用
public static void main(String[] args)
{
//注意索引是从0 开始的
List<String> list=new ArrayList<>();
//下面的方法还是当Collection 使用
list.add("Crystal");
list.add("Destiny");
list.add("刘亦菲");
list.add("刘诗诗");
System.out.println(list);
//在索引为3的位置插入元素
list.add(3,"刘茜茜");
System.out.println(list);
//替换索引为3的位置的元素
list.set(3,"刘东东");
System.out.println(list);
//删除索引为3的位置的元素
list.remove(3);
System.out.println(list);
//获取List的长度
System.out.println(list.size());
for(int i=0;i<list.size();i++)
{
//获取索引为i的位置的元素
System.out.println(list.get(i));
}
}
}
运行结果:
(注意运行结果的对应关系)
[Crystal, Destiny, 刘亦菲, 刘诗诗]
[Crystal, Destiny, 刘亦菲, 刘茜茜, 刘诗诗]
[Crystal, Destiny, 刘亦菲, 刘东东, 刘诗诗]
[Crystal, Destiny, 刘亦菲, 刘诗诗]
4
Crystal
Destiny
刘亦菲
刘诗诗
三.Queue集合类(接口)
Queue集合类只有一个子接口 ——Deque集合(接口)。
① Deque 集合 是功能被限制了的线性表。
② Deque 集合的两个实现类:
ArrayDeque —— 基于数组实现
LinkedList —— 基于链表实现
示例程序:
示例1:Deque栈的用法(先进后出)
import java.util.*;
public class DequeTest
{
public static void main(String[] args)
{
Deque<String> deque = new ArrayDeque<>();
//将四个元素压入栈中
deque.push("第一个元素");
deque.push("第二个元素");
deque.push("第三个元素");
deque.push("第四个元素");
//调用 pop()方法弹出栈顶元素
//注意元素弹出后就“不再存在于栈里面了”
for(int i=0;i<4;i++)
{
//deque.size()返回元素个数
System.out.println("当前站里面的元素个数是 " + deque.size());
//deque.peek()浏览元素,元素未弹出
System.out.println(" 浏览 " + deque.peek());
//deque.pop()弹出元素
System.out.println(" 弹出 " + deque.pop());
}
//执行此行代码将抛出异常
System.out.println( +deque.pop());
}
}
运行结果:
当前站里面的元素个数是 4
浏览 第四个元素
弹出 第四个元素
当前站里面的元素个数是 3
浏览 第三个元素
弹出 第三个元素
当前站里面的元素个数是 2
浏览 第二个元素
弹出 第二个元素
当前站里面的元素个数是 1
浏览不弹出 第一个元素
弹出 第一个元素
Exception in thread "main" java.util.NoSuchElementException
……
示例2:Deque队列的用法(先进先出)
import java.util.*;
public class ListTest
{
public static void main(String[] args)
{
Deque<String> deque = new ArrayDeque<>();
//从队列尾部添加元素
deque.offer("第一个元素");
deque.offer("第二个元素");
deque.offer("第三个元素");
deque.offer("第四个元素");
//从队头取出元素,元素出队后不再存在与队列里
for(int i=0;i<4;i++)
{
System.out.println("当前站里面的元素个数是 " + deque.size());
System.out.println("浏览 " + deque.peek());
System.out.println("取出 " + deque.poll());
}
//元素全部出队后,队列为空(null)
System.out.println("已经全部出队 对列为 " + deque.poll());
}
}
运行结果:
当前站里面的元素个数是 4
浏览 第一个元素
取出 第一个元素
当前站里面的元素个数是 3
浏览 第二个元素
取出 第二个元素
当前站里面的元素个数是 2
浏览 第三个元素
取出 第三个元素
当前站里面的元素个数是 1
浏览 第四个元素
取出 第四个元素
已经全部出队 对列为 null
四.操作集合的工具类:Collections
Collections —— 操作集合的类
Arrays —— 操作数组的类
Objects —— 操作对象的类
Collections 的重要方法:
shuffle(List<?> list) —— 将List 集合元素进行随机排列
synchronizedXxx —— 这个系列(Xxx不同)的方法是把原有的集合包装成线程安全的集合