Java SE第8章 Java集合

Java SE第8章 Java集合

1. 集合的概念和作用

集合主要用于存储数据,因此集合类也被称为容器类,集合里只能保存对象(对象的引用变量)

java集合类主要由两个接口派生而出: Collection 和 Map

Set,List都继承了 Collection,其中, Set元素不能重复, List元素可以重复 ,Map元素具有映射关系, key-valueCollection的部分方法

  • boolean retainAll(Collection c): 将调用该方法的集合变成该集合和集合c的交集
  • **Object[] toArray()😗*将集合转换成一个数组

所有的 Collection实现类都重写了 **toString()**方法

2. 使用Lambad表达式遍历集合

java 8 为 Iterable接口新增了一个 **forEach(Consumer action)**默认方法,参数是函数式接口, IterableCollection的父接口,当程序调用 **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的用法

TreeSetSorted接口的实现类,可以确保元素处于排序状态

  • **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还增加了向前迭代的功能

ArrayListVector都是基于数组的实现 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的用法

ArrayDequeDeque接口的一个实现类,基于数组实现,不指定大小时默认长度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对

LinkedHashMapHashMap的子类,底层维护双向链表

PropertiesHashtable子类,一般用于存储配置文件,key-value都为字符串

  • **void load(InputStream inStream):**装载配置文件
  • **void store(OutputStream out, String comments):**输出至配置文件
  • **Object setProperty(String key, String value):**设置属性值,相当于 put()

WeakHashMapHashMap用法基本相似,区别在于 HashMap中的key保留对实际对象的强引用,只要对象不销毁就不会自动删除,而 WeakHashMap保留对象弱引用,这些key没有被强引用引用时就可能会被垃圾回收

IdentityHashMapHashMap相似,只是在处理 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 的集合体系

Collection
Map

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值