一、集合
1、集合和数组的区别
(1)、长度区别
- 数组固定
- 长度可变
(2)、内容区别
- 数组可以是基本数据类型,也可以是引用数据类型
- 集合只能是引用类型--
(3)、元素内容
- 数组只能存储同一种类型
- 集合可以存储不同类型
2、collection集合的方法
Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可以用于操作Set集合,也可用于操作List和Queue集合。Collection接口里定义了如下操作集合元素的方法:
- boolean add(Object o):该方法用于向集合中添加一个元素。如果集合对象被添加操作改变了,则返回true
- boolean addAll(Collection c):该方法把集合c里的所有元素添加到指定集合里。如果集合对象被添加操作改变了,则返回true。
- void clear():清楚集合里所有元素,将集合长度变为0。
- boolean contains(Object o):返回集合里是否包含指定元素
- boolean containsAll(Collection c):返回集合里是否包含集合c里的所有元素
- boolean isEmpty():返回集合是否为空。当集合长度为0时返回true,否则返回flase
- Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素
- boolean remove(Object o):删除集合中的指定元素o,当集合中包含一个或多个元素o时,该方法只删除第一个符合条件的元素,该方法返回true。
- boolean removeAll(Collection c):从集合中删除集合c里包含的所有元素(相当于把调用该方法的集合减集合c),如果删除了一个或一个以上的元素,则该方法返回true
- boolean retainAll(Collection c):从集合中删除集合c里不包含的元素(相当于把调用该方法的集合变成该集合和集合c的交集),如果该操作改变了调用该方法的集合,则该方法返回true。
- int size():该方法返回集合里元素的个数。
- Object[] toArray():该方法把集合转换成一个数组,所有集合元素变成对应的数组元素。
创建两个Collection对象,一个是c集合,一个是books集合,其中c集合是ArrayList,而books集合是HashSet,虽然使用的实现类不同,但当把他们当成Collection来使用时,使用add、remove、clear等方法来操作集合元素时没有任何区别。
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class CollectionTest{
public static void main(String[] args) {
Collection c = new ArrayList();
//添加元素
c.add(6);
//虽然集合里不能放基本类型的值,但java支持自动装箱
c.add(6);
System.out.println("c集合里的元素个数为:"+c.size()); //输出2
//删除指定元素
c.remove(6);
System.out.println("c集合里的元素个数为:"+c.size()); //输出1
//判断是否包含指定字符串
System.out.println("c集合是否包含\"ABC\"字符串:"+c.contains("ABC")); //输出true
c.add("语文课程");
System.out.println("c集合里的元素:"+c);
Collection books = new HashSet();
books.add("英语课程");
books.add("数学课程");
System.out.println("c集合是否完全包含books集合?"+c.containsAll(books)); //输出flase
//用c集合减去books集合里的元素
c.removeAll(books);
System.out.println("c集合里的元素:"+c);
c.clear();
System.out.println("books集合里的元素"+c);
//控制books集合里只剩下c集合里也包含的元素
books.retainAll(c);
System.out.println("books集合里的元素:"+books);
}
}
输出:
c集合里的元素个数为:2
c集合里的元素个数为:1
c集合是否包含"ABC"字符串:false
c集合里的元素:[6, 语文课程]
c集合是否完全包含books集合?false
c集合里的元素:[6, 语文课程]
books集合里的元素[]
books集合里的元素:[]
3、常用集合的分类
Collection 接口的接口 对象的集合(单列集合):
List 接口:元素按进入先后有序保存,可重复
LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
Vector 接口实现类 数组, 同步, 线程安全
Stack 是Vector类的实现类
Set 接口: 仅接收一次,不可重复,并做内部排序
HashSet 使用hash表(数组)存储元素
LinkedHashSet 链表维护元素的插入次序
TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合)
Hashtable 接口实现类, 同步, 线程安全
HashMap 接口实现类 ,没有同步, 线程不安全
LinkedHashMap 双向链表和哈希表实现
WeakHashMap
TreeMap 红黑树对所有的key进行排序
IdentifyHashMap
4、遍历集合
(1)、使用Lambda表达式遍历集合
Iterator接口新增了一个forEach(Consumer action)默认方法,该方法所需要参数的类型是一个函数式接口。而Iterator接口是Collection接口的父接口,因此Collection集合也可以直接调用该方法。
import java.util.Collection;
import java.util.HashSet;
public class CollectionEach{
public static void main(String[] args) {
//创建一个集合
Collection books = new HashSet();
books.add("字符串1");
books.add("字符串2");
books.add("字符串3");
//调用forEach()方法遍历集合
books.forEach(obj->System.out.println("迭代集合元素:"+obj));
}
}
输出:
迭代集合元素:字符串2
迭代集合元素:字符串1
迭代集合元素:字符串3
(2)、使用Iterator遍历集合
Iterator接口定义了如下四个方法:
- boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回true。
- Object next():返回集合里的下一个元素。
- void remove():删除集合里上一次next方法返回的元素
- void forEachRemaining(Consumer action):java 8为Iterator新增的默认方法,该方法可使用Lambda表达式来遍历集合元素
Iterator必须依赖于Collection对象,一个Iterator对象必然有一个与之关联的Collection对象。Iterator提供了两个方法来迭代访问Collection集合里的元素,并可以通过remove()方法删除集合中上一次next()方法返回的集合元素。
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorTest{
public static void main(String[] args) {
//创建集合
Collection books = new HashSet();
books.add("字符串1");
books.add("字符串2");
books.add("字符串3");
//获取books集合对应的迭代器
Iterator it = books.iterator();
while(it.hasNext()) {
//it.next()方法返回的数据类型是Object类型,因此需要强制类型转换
String book = (String)it.next();
System.out.println(book);
if(book.equals("字符串")) {
//从集合中删除上一次next()方法返回的元素
it.remove();
}
//对book变量赋值,不会改变集合元素本身
book = "测试字符串";
}
System.out.println(books);
}
}
输出:
字符串2
字符串1
字符串3
[字符串2, 字符串1, 字符串3]
(3)、使用Lambda表达式遍历Iterator集合
java 8为Iterator新增了一个forEachRemaining(Consumer action)方法,该方法所需要的Consumer参数同样也是函数式接口。
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorEach{
public static void main(String[] args) {
//创建一个集合
Collection books = new HashSet();
books.add("字符串1");
books.add("字符串2");
books.add("字符串3");
//获取books集合对应的迭代器
Iterator it = books.iterator();
//使用Lambda表达式(目标类型是Comsumer)来遍历集合元素
it.forEachRemaining(obj->System.out.println("迭代集合元素:"+obj));
}
}
输出:
迭代集合元素:字符串2
迭代集合元素:字符串1
迭代集合元素:字符串3
(4)、使用foreach循环遍历元素
与使用Iterator接口迭代访问集合元素类似的是,foreach循环中的迭代变量也不是集合元素本身,系统只是以此把集合元素的值赋给迭代变量,因此在foreach循环中修改迭代变量的值也没有任何意思。同样,使用foreach循环迭代访问集合元素是,该集合也不能改变。
import java.util.Collection;
import java.util.HashSet;
public class FroeachTest{
public static void main(String[] args) {
//创建一个集合
Collection books = new HashSet();
books.add("字符串1");
books.add("字符串2");
books.add("字符串3");
for (Object obj:books) {
//此处的book变量也不是集合元素本身
String book = (String)obj;
System.out.println(book);
}
System.out.println(books);
}
}
输出:
字符串2
字符串1
字符串3
[字符串2, 字符串1, 字符串3]
二、list和set集合
1、list和set集合区别
(1)、有序性
- list保证按插入的顺序排序
- set存储和取出顺序不一致
(2)、唯一性
- list可以重复
- set元素唯一
(3)、获取元素
- list可以通过索引直接操作元素
- set不能根据索引获取元素
2、set集合
Set接口继承了Collcetion接口,包含Collcetion中的所有方法。
(1)、set接口的实现类
- HashSet类实现了Set接口,不保证Set的迭代顺序,特别是它不保证该顺序恒久不变,此类允许使用null元素
- TreeSet类不仅实现了Set接口,还实现了java.util.SortedSet接口,因此,TreeSet类实现的Set集合在遍历集合时按照自然顺序递增排序,也可以按照指定比较器递增排序
TreeSet类增加的方法
方法 | 功能描述 |
first() | 返回此Set中当前第一个(最低)元素 |
last() | 返回此Set中当前最后一个(最高)元素 |
comparator() | 返回对此Set中的元素进行排序的比较器;如果此Set使用自然顺粗,则返回null |
headSet(E toElement) | 返回一个新的Set集合,新集合是toElement(不包含)之前的所有对象 |
subSet(E fromElement,E fromElement) | 返回一个新的Set集合,是fromElement(包含)对象与fromElement(不包含)对象之间的所有对象 |
tailSet(E fromElement) | 返回一个新的Set集合,新集合包含对象fromElement(包含)之后的所有对象 |
(2)、Set特点
元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
(3)、HashSet类
HashSet是Set接口的典型表现,大多数时候使用set集合就是使用这个实现类。
HashSet按Hash算法来存储集合中的元素,因此具有很好的存储和查找性能。底层数据结构是哈希表。(无序,唯一) 如何来保证元素唯一性?
依赖两个方法:hashCode()和equals()
HashSet特点如下:
- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化
- HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程通知修改了HashSet集合时,则必须通过代码来保证其同步
- 集合元素值可以实null
如下程序提供了三各类A、B和C,他们分别重写了equals()、hasCode()两个方法的一个或全部。注意:当把一个对象放入HashSet中时,如果有重写该对象对应类的equals()方法,则也应该重写hashCode()方法。规则是:如果有两个对象通过equals()方法比较返回true,这两个对象的hashCode()值也应该相同。
import java.util.HashSet;
//类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,且重写其equals()方法总是返回true
class C{
public int hashCode() {
return 2;
}
public boolean equals(Object obj) {
return true;
}
}
public class HashSetTest{
public static void main(String[] args) {
HashSet books = new HashSet();
//分别向books集合中添加两个A对象、两个B对象、两个C对象
books.add(new A());
books.add(new A());
books.add(new B());
books.add(new B());
books.add(new C());
books.add(new C());
System.out.println(books);
}
}
输出:
[Text.B@1, Text.B@1, Text.C@2, Text.A@27973e9b, Text.A@5b6f7412]
(4)、LinkedHashSet
底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
(5)、TreeSet
底层数据结构采用二叉树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造),自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列,返回0说明两个对象相同,不需要存储;比较器排需要在TreeSet初始化是时候传入一个实现Comparator接口的比较器对象,或者采用匿名内部类的方式new一个Comparator对象,重写里面的compare()方法。
底层数据结构是红黑树。(唯一,有序)
1. 如何保证元素排序的呢?
自然排序
比较器排序
2.如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
3、List集合
list接口继承了Collcetion接口,包含Collcetion中的所有方法。
方法 | 功能描述 |
void add(int index,Object element) | 将元素elemet插入到List集合的index处 |
boolean addAll(int index,Collection c) | 将集合c所包含的所有元素都插入到List集合的index处 |
Object get(int index) | 返回集合index索引处的元素 |
int indexOf(Object o) | 返回兑现那个o在List集合中第一次出现的位置索引 |
int lastIndexOf(Object o) | 返回对象o在list集合中最后一次出现的位置索引 |
Object remove(int index) | 返回并删除index索引处的元素 |
Object set(int index,Object element) | 将index索引处的元素替换成element对象,返回被替换的旧元素 |
List subList(int forIndex,int toIndex) | 返回从索引fromIndex(包含)到索引toIndex(不包含)处所有集合元素组成的子集合 |
void replaceAll(UnaryOperator operator) | 根据operator指定的计算机规则重新设置List集合的所有元素 |
void sort(Comparator c) | 根据Comparator参数对List集合的元素排序 |
此外,list还定义了两种方法
- get(int int index):获得指定索引位置的元素
- set(int inedx,Object obj):缉拿该集合中指定索引位置的对象修改为指定的对象
(1)、List特点
元素有放入顺序,元素可重复
和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
(2)、List接口的实现类
a、ArrayList
实现了可变的数组,允许保存所有元素,包括null,并可以根据索引位置对集合进行快速的随机访问;缺点是向指定的索引位置插入对象或删除对象的速度较慢
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高
b、LinkedList
采用链表结构保存对象。优点是便于像集合中插入和删除对象;但对于随机访问集合中的对象,使用LinkedList类实现List集合的效率较低
优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
c、Vector
底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程安全,效率低
通过ArrayList、LinkedList类实例化List集合
List<E>List = new ArrayList<>();
List<E>List2 = new LinkedList<>();
创建集合对象,随机获取集合中的某个元素,然后移除数组中索引位置为”2“的元素,最后遍历数组
import java.util.*;
public class Demo {
public static void main(String[] args) {
List<String>list = new ArrayList<>(); //创建集合对象
list.add("a"); //向集合添加数据据
list.add("b");
list.add("c");
int i = (int)(Math.random()*list.size()); //获取0~~2之间的随机数;集合的索引也是从0开始
System.out.println("随机获取数组中的元素:"+ list.get(i));
list.remove(2); //将指定索引位置的元素从集合中移除
System.out.println("将索引是‘2'的元素从数组移除后,数组中的元素是:");
for(int j = 0 ; j < list.size() ; j++){ //循环遍历集合
System.out.println(list.get(j));
}
}
}
三、Map集合
1、Map集合
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
Map 没有继承 Collection 接口, Map 提供 key 到 value 的映射,你可以通过“键”查找“值”。一个 Map 中不能包含相同的 key ,每个 key 只能映射一个 value 。 Map 接口提供 3 种集合的视图, Map 的内容可以被当作一组 key 集合,一组 value 集合,或者一组 key-value 映射。
2、Map接口常用方法
3、Map接口的实现
- HashMap类是基于哈希表的Map接口的实现,此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
- TreeMap类不仅实现了Map接口,还实现了java.util.SortedMap接口,因此集合中的映射关系具有一定的顺序
3、TreeSet, LinkedHashSet and HashSet 的区别
(1)介绍
TreeSet, LinkedHashSet and HashSet 在java中都是实现Set的数据结构
- TreeSet的主要功能用于排序
- LinkedHashSet的主要功能用于保证FIFO即有序的集合(先进先出)
- HashSet只是通用的存储数据的集合
(2)、相同点
- Duplicates elements: 因为三者都实现Set interface,所以三者都不包含duplicate elements
- Thread safety: 三者都不是线程安全的,如果要使用线程安全可以Collections.synchronizedSet()
(3)、不同点
- Performance and Speed: HashSet插入数据最快,其次LinkHashSet,最慢的是TreeSet因为内部实现排序
- Ordering: HashSet不保证有序,LinkHashSet保证FIFO即按插入顺序排序,TreeSet安装内部实现排序,也可以自定义排序规则
- null:HashSet和LinkHashSet允许存在null数据,但是TreeSet中插入null数据时会报NullPointerException
注意:
- Hashtable是线程安全的,HashMap不是线程安全的。
- HashMap效率较高,Hashtable效率较低。 如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。
- Hashtable不允许null值,HashMap允许null值(key和value都允许)
- 父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap
四、Queue集合
Queue接口与List、Set同一级别,都是继承了Collection接口。
Queue用于模拟队列这种数据结构,队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。Queue中定义了如下几个方法:
- void add(Object e)
- Object element()
- boolean offer(Object e)
- Object peek()
- Object poll()
- Object remove()
五、PriorityQueue实现类
PriorityQueue保存队列元素的顺序并不是按加入时的顺序,而是按照队列怨怒的大小进行重新排序。因此调用poll()或者peek()方法取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。
PriorityQueue不允许插入null元素,它还需要对队列元素进行排序,PriorityQueue的元素有两种排序方式:
- 自然排序
- 定制排序
代码:
import java.util.PriorityQueue;
public class PriorityQueueTest{
public static void main(String[] args) {
PriorityQueue pq = new PriorityQueue();
//依次向pq中加入四个元素
pq.offer(6);
pq.offer(-3);
pq.offer(20);
pq.offer(18);
//输出pq队列,并不是按元素的加入顺序排列
System.out.println(pq);
//访问队列的第一个元素,其实就是队列中最小的元素:-3
System.out.println(pq.poll());
}
}