Java SE第8章 Java集合
- 1. 集合的概念和作用
- 2. 使用Lambad表达式遍历集合
- 3.Collection集合的常规用法
- 4. 使用Predicate操作集合
- 5.使用Iterator和foreach循环遍历Collection集合
- 6. HashSet,LinkedHashSet用法
- 7.对集合使用Stream进行流式编程
- 8. EnumSet的用法
- 9. TreeSet的用法
- 10. ArrayList和Vector
- 11. List集合的常规用法
- 12. Queue接口与Deque接口
- 13.固定长度的List集合
- 14. ArrayDeque的用法
- 15. PriorityQueue的用法
- 16. Map的概念和常规用法
- 17. LinkedList 集合的用法
- 18. TreeMap的用法
- 19. 几种特殊的Map实现类
- 20. Hash算法对HashSet,HashMap性能的影响
- 21. Collection工具类的用法
- 22. Java 9 新增的不可变集合
- 23. Enumeration迭代器的用法
- 24. Java 的集合体系
1. 集合的概念和作用
集合主要用于存储数据,因此集合类也被称为容器类,集合里只能保存对象(对象的引用变量)
java集合类主要由两个接口派生而出: Collection 和 Map
Set,List都继承了 Collection,其中, Set元素不能重复, List元素可以重复 ,Map元素具有映射关系, key-value,Collection的部分方法
- boolean retainAll(Collection c): 将调用该方法的集合变成该集合和集合c的交集
- **Object[] toArray()😗*将集合转换成一个数组
所有的 Collection实现类都重写了 **toString()**方法
2. 使用Lambad表达式遍历集合
java 8 为 Iterable接口新增了一个 **forEach(Consumer action)**默认方法,参数是函数式接口, Iterable是 Collection的父接口,当程序调用 **forEach(Consumer action)**遍历集合时,程序会一次将集合元素传给 Consumer的 **accept(T t)**方法
Set books = new HashSet();
books.add("dasd");
books.add("xcz");
books.forEach(obj -> System.out.printlnJ(obj));
迭代器-> Iterator ,主要用于遍历 Collection集合元素
- **boolean hasNext()😗*没遍历完返回 false
- **Object next()😗*返回集合下一个元素
- **void remove():**删除集合上一次next方法返回的元素
- **void forEachRemaining(Consumer action) 😗*Java 8 新增方法,用于遍历集合
Iterator it = books.iterator();
while(it.hasNext()){
String book = (String)it.next();
}
使用迭代器遍历集合时,只能通过 **Iterator的remove()**方法,如果直接用集合 remove(),会直接引发异常
Iterator it = books.iterator();
//使用 Lambda表达式遍历集合
it.forEachRemaining(obj -> System.out.println(obj));
也可以使用 foreach循环遍历,底层还是迭代器,集合也不能改变
4. 使用Predicate操作集合
java 8 为 Collection集合新增了一个 **removeIf(predicate filter)**方法,会批量删除符合 filter条件的所有元素
books.removeIf(ele -> ((String)ele).length() < 10);//删除所有长度小于10的元素
6. HashSet,LinkedHashSet用法
HashSet不是同步的,集合元素可以是 null,当加入一个元素时,HashSet会调用该对象的 **hashCode()**方法得到该对象的 hashCode值,然后计算其下标, HashSet集合判断两个元素相等的标准是两个元素通过 **equals()和 hashCode()**返回都为 true,应保证同一个元素这两个返回值都为true
HashSet底层还是 HashMap(数组+链表+红黑树)
同时不要去修改元素的值
LinkedHashSet底层 LinkedHashMap(数组+双向链表),输出顺序和添加顺序一致,性能略低于 HashSet
8. EnumSet的用法
EnumSet中所有元素都必须是指定枚举类型的枚举值,元素也是有序的,根据在枚举类里的定义顺序排序
不允许加入 null,EnumSet没有暴露构造器,只能通过类方法创建对象
- **EnumSet allOf(Class elementType)😗*创建一个包含指定枚举类所有枚举值的集合
- **EnumSet complementOf(Enumset s)😗*新的集合包含指定集合没有的所有的枚举值
- **EnumSet copyOf(Collection c)😗*使用一个普通集合创建 EnumSet集合
当试图复制一个 Collection集合里的元素创建 EnumSet集合时,必须保证 Collection集合里所有元素都是同一个枚举类的枚举值
9. TreeSet的用法
TreeSet是 Sorted接口的实现类,可以确保元素处于排序状态
- **Comparator comparator()😗*如果采用定制排序,返回定制排序的 comparator,否则返回 null
- **SortedSet subSet(Object fromElement, Object toElement)😗*返回 Set的子集合,范围左闭右开
自然排序时,如果把一个对象添加到 TreeSet,则该对象 的类必须实现 Comparable接口,否则会出现异常
同时,向 TreeSet中添加的必须是同一个类的对象
当一个对象加入到 TreeSet中时, TreeSet调用该对象的 **compareTo(Object obj)**方法与容器内其他对象比较大小,两个对象相等的唯一标准是通过 **compareTo(Object obj)**方法返回0,应确保 equals()也返回 true,
TreeSet可以删除没有被修改实例变量,且不与其他被修改实例变量的对象重复的对象
定制排序可以使用 Lambda表达式传入一个 Comparator对象
TreeSet,HashSet,EnunSet都是线程不安全的,当多个线程同时访问一个 Set集合,并且有超过一个线程修改了 Set集合,必须手动保证同步,最好在创建时进行
SortedSet s = Collectons.synchronizedSortedSet(new TreeSet());
10. ArrayList和Vector
List集合代表一个元素有序,可重复的集合
java 8 为 List接口添加两个默认方法
- **void replaceAll(UnaryOperator operator)😗*根据 operator指定的规则重新设置 List集合的所有元素
- **void sort(Comparator c)😗*根据 Comparator参数对 List集合元素排序
插入元素也使用 add(int index, Object element),List判断两个对象相等只要 equals()返回true
当程序试图删除一个元素时,List会调用该元素的equals()方法依次与集合元素进行比较
List还提供了一个 **listIterator()**方法,该方法返回一个 ListIterator对象,ListIterator接口继承了 Iterator接口,增加了如下方法
- **boolean hasPrevioue():**返回迭代器关联的集合是否还有上一个元素
- **Object previous()😗*返回迭代器的上一个元素
- **void add(Object o):**在指定位置插入一个元素
ListIterator相比于 Iterator还增加了向前迭代的功能
ArrayList和 Vector都是基于数组的实现 List类,封装了一个 Object[],都使用 initialCapacity参数设置该数组的长度,当添加元素超过数组长度时,它们的 initialCapacity会自动增加
如果添加大量元素时,可以使用 **ensureCapacity(int Capacity)**一次性增加 initialCapacity,减少分配次数,提高性能,不指定则默认大小为10
- void trimToSize():调整集合的 **Object[]**长度为当前元素个数
Vector还提供一个 Stack子类
- **Object peek()😗*返回栈第一个元素,不是出栈
- **Object pop(J)😗*返回栈第一个元素,并出栈
- **void push(Object item)😗*进栈
因为Stack是继承 Vector类的,所以性能较差,推荐使用 ArrayDeque代替
11. List集合的常规用法
Arrays类里有一个 **asList(Object…a)**方法,可以把一个数组转换成一个 List集合,这个 List集合既不是 ArrayList实现类也不是 Vector实现类,而是 Arrays的内部类 ArrayList的实例
Arrays.ArrayList是一个固定长度的 List集合,只能遍历,不能修改
12. Queue接口与Deque接口
Queue是模拟队列这种数据结构
- **boolean offer(Object e)😗*将元素加入到队列尾部,当使用有容量限制的队列时,此方法通常比 **add(Object e)**好
- **Object peek()😗*获取队列头部元素但不删除,如果队列为空,返回 null
- **Object poll()😗*获取队列头部元素且删除,如果队列为空,返回 null
- **Object remove():**获取队列头部元素,并删除该元素
Deque代表双端队列
当队列时
addLast(e)/offerLase(e)
removeFirst()/pollFirst()
getfirst()/peekFirst()
当栈时
addFirst(e)/offerFirst(e)
removeFirst()/pollfirst()
getFirst()/peekFirst()
14. ArrayDeque的用法
ArrayDeque是 Deque接口的一个实现类,基于数组实现,不指定大小时默认长度16
推荐当栈实现的集合, push(Object c)和pop(),当队列时 ,offer(Object c) 和 poll()
15. PriorityQueue的用法
PriorityQueue是一个比较标准的队列实现类,保证队列元素的顺序不是加入队列的顺序,而是按队列元素的大小重新排序,当调用 peek()或 poll()方法取出队列元素时,会取出最小的元素
同样有两种排序,自然排序和定制排序,自然排序需要集合中的元素实现 Comparable接口,而且应该是同一个类的多个实例
16. Map的概念和常规用法
Map用于保存具有映射关系的数据
- **Set entrySet():**返回 Map中包含的 key-value组成的 Set集合,每个集合元素都是 Map.Entry对象
- **Set keySet():**返回 Map中所有 key组成的 Set集合
- **boolean remove(Object key,Object value):**java 8 新增方法,删除指定key->value对,成功删除返回true,否则返回false
- **Collection values():**返回所有 value组成的 Collection
Map中有一个内部类 Entry,该类封装了一个 key-value对
- **Object geKey():**返回 Entry包含的 key
- **Object getValue():**返回 Entry包含的 value
- **Object setValue(V Value):**设置value,返回新设置的 value
放入重复的key时,新的 value会覆盖原来的 value,如果覆盖,就会返回原来的value
java 8新增部分方法
- **void forEach(BiConsumer action):**遍历key-value对
- **Object replace(Object key,Object value):**该方法不可能添加新的 key-value对,如果key不存在,就返回 null
- **boolean replace(K key, V oldValue, V newValue):**替换指定key-value对,替换失败返回false
- **replaceAll(BiFunction function):**该方法使用 BiFunction对原 key-value执行计算,并将计算结果作为新value
public class Map_ {
public static void main(String[] args) {
HashMap<Object, Object> map = new HashMap<>();
map.put("1", 1);
map.put("2", 2);
map.put("3", 3);
map.put("4", 4);
map.put("5", 5);
System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 5=5}
map.replaceAll((k,v) -> (int)v+6);
System.out.println(map);//{1=7, 2=8, 3=9, 4=10, 5=11}
}
}
17. LinkedList 集合的用法
底层双向链表,双端队列
可以根据索引来访问集合元素
books.get(0)
随机访问效率差,插入删除效率高
所有基于数组实现的集合,使用随机访问性能都比迭代器高,因为随机访问会被映射成对数组元素的访问
如果需要遍历List集合,对于基于数组实现的集合,推荐使用 随机访问方法(get)遍历,对于LinkedList集合,应该采用迭代器遍历
如果需要经常插入,删除,应该使用 LinkedList集合,因为使用 ArrayList,Vector可能需要经常重新分配内部数组的大小,效果可能较差
如果多个线程同时访问List集合中的元素,应该考虑使用 Collection将集合包装成线程安全的集合
18. TreeMap的用法
底层红黑树,保证有序,自然排序时需实现Comparable接口,需重写equals返回一致结果
- **NavigableMap subMao(Object fromKey, boolean fromInclusive, Object toKey, boolean toInclusive)😗*返回该Map的子Map
- **SortedMap subMap(Object fromKey, Object toKey):**返回该Map的子Map,左闭右开
19. 几种特殊的Map实现类
Hashtable是一个线程安全的类,不允许使用 null作为 key和value
HashMap可以使用 null作为key或value
为了成功在 HashMap,Hashtable中存储,获取对象,用作 key的对象必须实现 **hashCode()**方法和 **equals()**方法尽量不要使用可变对象当key,如果使用,就不要修改key,只能删除没被修改的key所对应的key-value对
LinkedHashMap是 HashMap的子类,底层维护双向链表
Properties是 Hashtable子类,一般用于存储配置文件,key-value都为字符串
- **void load(InputStream inStream):**装载配置文件
- **void store(OutputStream out, String comments):**输出至配置文件
- **Object setProperty(String key, String value):**设置属性值,相当于 put()
WeakHashMap与 HashMap用法基本相似,区别在于 HashMap中的key保留对实际对象的强引用,只要对象不销毁就不会自动删除,而 WeakHashMap保留对象弱引用,这些key没有被强引用引用时就可能会被垃圾回收
IdentityHashMap与 HashMap相似,只是在处理 key时,当且仅当 key1 == key2时才相等,也允许使用null作为键和值
EnumMap所有的key都必须是单个枚举类的枚举值,内部以数组形式保存,根据key的自然排序维护 key-value的顺序,不允许使用null当key
20. Hash算法对HashSet,HashMap性能的影响
TreeMap通常比 HashMap,Hashtable慢,因为采用红黑树,但 TreeMap有序,可以调用 **keySet()**再用 **toArray()**方法生成 key的数组,接下来使用 Arrays的 **binarySearch()**快速查找对象
HashSet,HashMap,Hashtable都使用 hash 算法决定元素的存储,因此它们的 hash表包含以下属性
容量(capacity):hash表中桶的数量
初始化容量(initial capacity):创建 hash表时桶的数量
尺寸(size):当前 hash表中记录的数量
负载因子(load factor):等于 size/capacity,负载因子为0表示空的hash表,0.5表示半满的hash表,轻负载的哈希表具有冲突少,适宜插入和查询的特点(但迭代器遍历慢)
除此之外还有一个负载极限(默认0.75),是一个0-1的数值,决定 hash表最大填满程度,当负载因子达到负载极限时,hash会自动成倍增加容量,并将原有的对象重新分配,放入新的桶里,称为rehashing
较高的负载极限可以可以降低 hash表占用的内存空间,但会增加查询数据的开销,较低的负载极限会提高查询的效率,但会增加内存开销
21. Collection工具类的用法
Collection中提供了大量操作集合的方法,还提供了将集合对象设置为不可变,对集合对象实现同步控制等方法
- **void shuffle(List list):**对list进行随机排序
- **void rotate(List list, int distance):**distance为正数时,将集合后distance个元素移到前面,为负数时,将集合前distance个元素移动到集合后面,不改变集合长度
梭哈游戏
package Seven_.jihe;
import com.sun.media.jfxmediaimpl.HostUtils;
import java.awt.*;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
/**
* 梭哈游戏
*/
public class ShowHand {
//定义该游戏最多支持多少玩家
private final int PLAY_NUM = 5;
//定义扑克牌的所有花色和数值
private String[] types = {"方块", "草花", "红心", "黑桃"};
private String[] values = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
//cards时一局游戏中剩下的扑克牌
private List<String> cards = new LinkedList<>();
//定义所有玩家
private String[] players = new String[PLAY_NUM];
//所有玩家手上的扑克牌
private List<String>[] playersCards = new List[PLAY_NUM];
/**
* 初始化扑克牌,放入52张扑克牌并用shuffle方法随机排序
*/
public void initCards(){
for(int i = 0;i < types.length;i++){
for(int j = 0;j < values.length;j++){
cards.add(types[i] + values[j]);
}
}
//随机排列
Collections.shuffle(cards);
}
/**
* 初始化玩家,为每个玩家分配用户名
*/
public void initPlayer(String... names){
if(names.length > PLAY_NUM || names.length < 2){
System.out.println("玩家数量不对");
return;
} else{
//初始化玩家用户名
for(int i = 0;i < names.length;i++){
players[i] = names[i];
}
}
}
/**
* 初始化玩家手上的扑克牌,开始游戏时每个玩家手上的扑克牌为空
* 程序使用一个 长度为0 的 Linkedlist表示
*/
public void initPlayerCards(){
for(int i = 0;i < players.length;i++){
if(players[i] != null && !players[i].equals("")){ //当玩家不为空
playersCards[i] = new LinkedList<String>();
}
}
}
/**
* 输出全部扑克牌
*/
public void showAllCards(){
for(Object card : cards){
System.out.println(card);
}
}
/**
* 派扑克牌
* @param first 最先派给谁
*/
public void deliverCard(String first){
//查询指定元素在数组中的索引
int firstPos = 0;
for(int i = 0;i < players.length;i++){
if(first.equals(players[i])){
firstPos = i;
break;
}
}
//依次给位于该玩家之后的每个玩家发牌
for(int i = firstPos;i < PLAY_NUM;i++){
if(players[i] != null){
playersCards[i].add(cards.get(0));
cards.remove(0);
}
}
//依次给位于该玩家之前的每个玩家发牌
for(int i = 0 ; i < firstPos;i++){
if(players[i] != null){
playersCards[i].add(cards.get(0));
cards.remove(0);
}
}
}
/**
* 输出玩家手上的扑克牌
* 实现该方法时,应该控制每个玩家看不到别人的第一张牌
*/
public void showPlayerCards(){
for(int i = 0;i < PLAY_NUM;i++){
//当玩家不为null时
if(players[i] != null) {
//输出玩家
System.out.println(players[i] + " : ");
//遍历输出玩家手上的扑克牌
for (Object card : playersCards[i]) {
System.out.println(card + "\t");
}
}
System.out.println("\n");
}
}
public static void main(String[] args) {
ShowHand sh = new ShowHand();
sh.initPlayer("12", "23", "34");
sh.initCards();
sh.initPlayerCards();
sh.showAllCards();
System.out.println("-------------------");
//开始派牌
sh.deliverCard("12");
sh.showPlayerCards();
}
}
- **void fill(List list, Object obj)😗*使用 obj替换 list中所有元素
- **int frequency(Collection c, Object o)😗*返回集合中指定元素出现次数
- **boolean replaceAll(List list, Object oldVal, Object newVal)😗*使用newVal替换List对象所有oldVal
- **int binarySearch(List list, Object key):**使用二分搜索法搜索指定的List集合,获得key的索引,必须保证语句处于有序状态
**Collection **提供了多个 **synchronizedXxx()**方法将指定集合包装成线程同步的集合
22. Java 9 新增的不可变集合
Collection提供三个类方法返回一个不可变集合
- **emptyXxx():**返回一个空的,不可变集合
- **singletonXxx():**返回一个只包含指定对象(只有一个或一项元素的),不可变的集合对象,可以时List或Map
- **unmodifiableXxx():**返回指定集合对象的不可变版本
不可变的集合对象只能访问集合元素,不能修改集合元素
java 9 新增了不可变集合
//创建包含4个元素的不可变集合
Set set = Set.of("java", "kasd", "asca", "xcvv");
//不可变集合不能修改
//创建包含3个 key-value对的Map集合
Map map = Map.of("sad",1,"zxc",2, "c", 3);
//使用 Map.entry()方法显式构建 key-value对
Map map2 = Map.ofEntries(Map.entry("语文", 29),Map.entry("数学", 123), Map.entry("英语", 8));
23. Enumeration迭代器的用法
**Enumeration **接口是 iterator迭代器的古老版本,尽量别用
- **boolean hasMoreElements()😗*判断是否还有下一个元素
- **Object nextElement()😗*返回迭代器的下一个元素
24. Java 的集合体系