ArrayList

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。

Collection

Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。

package array.demo1;

import java.util.ArrayList;
import java.util.Collection;

public class Test {
    public static void main(String[] args) {
        Collection<String> s = new ArrayList<String>();

        //添加元素
        s.add("hello");
        s.add("world");
        s.add("!");
        System.out.println(s);

        //移除元素
        s.remove("!");
        System.out.println(s);

        //判断集合中是否存在元素
        System.out.println(s.contains("hello"));

        //判断集合是否为空
        System.out.println(s.isEmpty());

        //输出集合的长度
        System.out.println(s.size());
    }
}

/**
[hello, world, !]
[hello, world]
true
false
2
**/
Iterator

Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。

  • next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
  • hasNext() 用于检测集合中是否还有元素。
  • remove() 将迭代器返回的元素删除。
package array.demo2;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class App {
    public static void main(String[] args) {
        Collection<Student> s = new ArrayList<Student>();

        Student s1 =  new Student("张三", 8);
        Student s2 =  new Student("李四", 9);
        Student s3 =  new Student("王五", 10);

        s.add(s1);
        s.add(s2);
        s.add(s3);

        Iterator<Student> it = s.iterator();
        while (it.hasNext()){
            Student student= it.next();
            System.out.println(student.getName() +", "+ student.getAge());
        }

    }
}

List

有序集合(也称为序列)。该界面的用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。
与集合不同,列表通常允许重复的元素。更正式地,列表通常允许成对的元素e1和e2,使得e1.equals (e2),并且如果它们允许空元素,它们通常允许多个空元素。有人可能希望实现一个禁止重复的列表,当用户尝试插入时会抛出运行时异常,但是我们预计这种使用是罕见的。

package array.demo3;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("!");
        System.out.println(list);

        //根据位置添加函数
        list.add(2, "java");
        System.out.println(list);

        //根据位置返回元素
        String s = list.get(1);
        System.out.println(s);
        System.out.println(list);

        //删除指定位置元素 返回被删除元素
        s = list.remove(2);
        System.out.println(s);
        System.out.println(list);

        //修改指定位置元素 返回被修改元素
        s = list.set(2, "!!!");
        System.out.println(s);
        System.out.println(list);

        //索引遍历
        for (int i = 0; i < list.size(); i++) {
            s = list.get(i);
            System.out.print(s+' ');
        }

        System.out.println();
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String i = it.next();
            System.out.print(i+' ');
        }
    }
}

/**
[hello, world, !]
[hello, world, java, !]
world
[hello, world, java, !]
java
[hello, world, !]
!
[hello, world, !!!]
hello world !!! 
hello world !!! 
**/

并发修改异常

package array.demo3;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("hello");
        list.add("world");
        list.add("!");
        System.out.println(list);

        Iterator<String> it = list.iterator();

        while (it.hasNext()){
            String s = it.next();
            if (s.equals("world")){
                list.add("java");
            }
        }
    }
}

报错

image-20210712162819082

查看源码

//   java/util/ArrayList.java
public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    ......

    /**
    判断 modCount 是否等于 expectedModCount 否则返回错误
    modCount:实际修改次数
    expectedModCount:预期修改次数
    **/
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致

可以使用集合遍历来对对象进行操作

Listlterator

列表迭代器,通过List集合的listlterator()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。

  • E next():返回迭代中的下一个元素
  • boolean hasNext():如果迭代具有更多元素,则返回trueE
  • previous():返回列表中的上一个元素
  • boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
  • void add(E e):将指定的元素插入列表
package array.demo3;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Test3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("hello");
        list.add("world");
        list.add("!");
        System.out.println(list);

        ListIterator<String> it = list.listIterator();

        while(it.hasNext()){
            String s = it.next();
            if (s.equals("hello")){
                it.add("java");
            }
            System.out.println(s);
        }
        System.out.println(list);

        while(it.hasPrevious()){
            String s = it.previous();
            System.out.println(s);
        }

    }
}

增强for循环

继承自接口lterator ,也会导致并发修改异常

package array.demo3;

import java.util.ArrayList;
import java.util.List;

public class Test4 {
    public static void main(String[] args) {
        int[] intArr = {1,2,3,4,5};
        for (int i : intArr){
            System.out.print(i+" ");
        }

        System.out.println("\n--------------------");
        String[] strArr = {"hello", "world", "!"};
        for (String s : strArr){
            System.out.print(s+" ");
        }

        System.out.println("\n--------------------");
        List<String> strList = new ArrayList<String>();
        strList.add("hello");
        strList.add("world");
        strList.add("!");
        for (String s :strList){
            System.out.print(s+" ");
        }
    }
}

/**
1 2 3 4 5 
--------------------
hello world ! 
--------------------
hello world ! 
**/

Linklist

ArrayList: 底层数据结构是数组,查询快,增删慢

LinkedList: 底层数据结构是链表,查询慢,增删快

  • public void addFirst(Ee) 在该列表开头插入指定的元素
  • public void addLast(E e) 将指定的元素追加到此列表的末尾
  • publicE getFirst() 返回此列表中的第一个元素
  • public E getLast(J) 返回此列表中的最后一个元素
  • public E removeFirst() 从此列表中删除并返回第一个元素
  • public E removeLast() 从此列表中删除并返回最后一个元素
package array.demo3;

import java.util.LinkedList;

public class Test5 {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<String>();

        list.add("hello");
        list.add("world");
        list.add("!");
        System.out.println(list);

        list.addFirst("Bob");
        list.addLast("java");
        System.out.println(list);

        System.out.println(list.getFirst());
        System.out.println(list.getLast());

        list.removeFirst();
        System.out.println(list);

        list.removeLast();
        System.out.println(list);
    }
}

/**
[hello, world, !]
[Bob, hello, world, !, java]
Bob
java
[hello, world, !, java]
[hello, world, !]
**/
Set

set,集合。不允许出现重复元素、集合中的元素位置无顺序、有且只有一个值为null的元素。

互异性:一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。
无序性:一个集合中,每个元素的地位都是相同的,元素之间是无序的。集合上可以定义序关系,定义了序关系后,元素之间就可以按照序关系排序。但就集合本身的特性而言,元素之间没有必然的序。
空集的性质:空集是一切集合的子集

package array.demo4;

import java.util.HashSet;
import java.util.Set;

public class Test {
    public static void main(String[] args) {
        Set<String> s = new HashSet<String>();

        s.add("hello");
        s.add("world");
        s.add("!");
        s.add("hello");

        System.out.println(s);
    }
}
/**
[!, world, hello]
**/

哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中的public int hashCode(): 返回对象的哈希码值

同一个对象多次调用hashCode0方法返回的哈希值是相同的

package array.demo4;

import java.util.HashSet;
import java.util.Set;

public class Test {
    public static void main(String[] args) {
        Set<String> s = new HashSet<String>();

        Student s1 = new Student("Bob", 18);
        System.out.println(s1.hashCode());
        System.out.println(s1.hashCode());

        //默认情况下不同对象的哈希值不同
        Student s2 = new Student("Bob", 18);
        System.out.println(s2.hashCode());  
    }
}
/**
2129789493
2129789493
668386784
**/

特点

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以是不包含重复元素的集合

linkedhash

  • 哈希表和链表实现的set接口,具有可预测的迭代次序
  • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  • 由哈希表保证元素唯一,也就是说没有重复的元素
package array.demo4;

import java.util.LinkedHashSet;

public class Test2 {
    public static void main(String[] args) {
        LinkedHashSet<String> l = new LinkedHashSet<String>();

        l.add("hello");
        l.add("world");
        l.add("java");

        for (String s : l){
            System.out.println(s);
        }

    }
}

/**
hello
world
java
**/

TreeSet

  • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
  • TreeSet():根据其元素的自然非序进行排序
  • TreeSet(Comparator comparator):根据指定的比较器进行排序
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以不包含重复元素的集合
package array.demo4;

import java.util.TreeSet;

public class Test3 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();

        ts.add(10);
        ts.add(40);
        ts.add(20);
        ts.add(50);
        ts.add(30);
        
        ts.add(50);

        for (Integer i : ts){
            System.out.println(i);
        }
    }
}
/**
10
20
30
40
50
**/

CompareTo

compareTo() 方法用于两种方式的比较:字符串与对象进行比较、按字典顺序比较两个字符串。

  • 如果参数字符串等于此字符串,则返回值 0;
  • 如果此字符串小于字符串参数,则返回一个小于 0 的值;
  • 如果此字符串大于字符串参数,则返回一个大于 0 的值。
package array.demo4;

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Student s){
        int num = this.age - s.age;  //先对年龄进行排序
        int num2 = num == 0 ? this.name.compareTo(s.name):num;
        return num2;
    }
}
package array.demo4;

import java.util.TreeSet;

public class Test4 {
    public static void main(String[] args) {
        Student s1 = new Student("Bob", 18);
        Student s2 = new Student("Jack", 19);
        Student s3 = new Student("Alice", 16);
        Student s4 = new Student("Tom", 24);

        TreeSet<Student> ts = new TreeSet<Student>();
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        for (Student s : ts){
            System.out.println(s.getName() + " : " + s.getAge());
        }
    }
}

/**
Alice : 16
Bob : 18
Jack : 19
Tom : 24
**/
泛型

泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口

泛型定义格式

  • <类型>:指定一种类型的格式。这里的类型可以看成是形参
  • <类型1,类型2.…>︰指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
  • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型

泛型的好处

  • 把运行时期的问题提前到了编译期间
  • 避免了强制类型转换
package array.demo5;

public class Generic <T>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

}
package array.demo5;

public class test {
    public static void main(String[] args) {
        Generic c1 = new Generic<String>();
        c1.setT("Bob");
        System.out.println(c1.getT());

        Generic c2 = new Generic<Integer>();
        c2.setT(26);
        System.out.println(c2.getT());
    }
}

泛型方法

泛型方法的定义格式:
修饰符<类型>返回值类型方法名(类型变量名){}

范例: public void show(T t){}

package array.demo5;

/**
public class Generic2 {
    public void show(String s){
        System.out.println(s);
    }

    public void show(Integer i){
        System.out.println(i);
    }

    public void show(Boolean b){
        System.out.println(b);
    }
}
 **/

//泛型改进
/**
 public class Generic2<T>{
     public <T> void show(T t){
        System.out.println(t);
     }

 }
 **/

//泛型方法
public class Generic2 {
    public <T> void show(T t){
        System.out.println(t);
    }

}

类型通配符

  • 类型通配符:<?>
    List<?>: 表示元素类型未知的List,它的元素可以匹配任何的类型
    这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

  • 类型通配符上限:<? extends类型>
    List<? extends Number>:它表示的类型是Number或者其子类型

  • 类型通配符下限:<? super类型>

    list<? super Number>:它表示的类型是Number或者其父类型

package array.demo5;

import java.util.List;
import java.util.ArrayList;

public class Test3 {
    public static void main(String[] args) {
        //<?>
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();

        //<? extends 类型>
 //       List<? extends Number> list4 = new ArrayList<Object>();
        List<? extends Number> list5 = new ArrayList<Number>();
        List<? extends Number> list6 = new ArrayList<Integer>();

        //<? super 类型>
        List<? super Number> list7 = new ArrayList<Object>();
        List<? super Number> list8 = new ArrayList<Number>();
//        List<? super Number> list9 = new ArrayList<Integer>();
    }
}

可变参数

Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项。当可变参数个数多余一个时,必将有一个不是最后一项,所以只支持有一个可变参数。因为参数个数不定,所以当其后边还有相同类型参数时,java无法区分传入的参数属于前一个可变参数还是后边的参数,所以只能让可变参数位于最后一项。

package array.demo5;

public class Test4 {
    public static void main(String[] args) {
        System.out.println(sum(10, 20));
        System.out.println(sum(10, 20, 30));
        System.out.println(sum(10, 20, 30, 40));
        System.out.println(sum(10, 20, 30, 40, 50));
    }

    public static int sum(int ...a){
        int sum = 0;
        for (int i : a ){
            sum += i;
        }

        return sum;
    }
}

可变参数应用

  • public static List asList(T... a): 返回由指定数组支持的固定大小的列表 返回的集合不能做增册操作,可以做修改操作
  • List接口中有一个静态方法 public static List of(E... elements):返回包含任意数量元素的不可变列表返回的集合不能做增删改操作
  • Set接口中有一个静态方法:
    public static Set of(E... elements):返回一个包含任意数量元素的不可变集合在给元素的时候,不能给重复的元素
    返回的集合不能做增删操作,没有修改的方法
package array.demo5;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class Test5 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world", "java");
        //list.add("a");
        //list.remove("java");
        list.set(2, "javaee");
        System.out.println(list);

        List<String> list2 = List.of("hello", "world", "java");
        //list2.add("a");
        //list2.remove("java");
        //list2.set(2, "javaee");

        Set<String> set = Set.of("hello", "world", "java");  //不能有重复参数
        //set.add("a");
        //set.remove("java");
        //set.set(2, "javaee");
        System.out.println(set);
    }

}
Map

Map 接口中键和值一一映射. 可以通过键来获取值。

  • 给定一个键和一个值,你可以将该值存储在一个 Map 对象。之后,你可以通过键来访问对应的值。
  • 当访问的值不存在的时候,方法就会抛出一个 NoSuchElementException 异常。
  • 当对象的类型和 Map 里元素类型不兼容的时候,就会抛出一个 ClassCastException 异常。
  • 当在不允许使用 Null 对象的 Map 中使用 Null 对象,会抛出一个 NullPointerException 异常。
  • 当尝试修改一个只读的 Map 时,会抛出一个 UnsupportedOperationException 异常。

相关方法

  • put(K key,V value) 添加元素
  • remove(Object key) 根据键删除键值对元素
  • void clear() 移除所有的键值对元素
  • boolean containsKey(Object key) 判断集合是否包含指定的键
  • boolean containsValue(Object value) 判断集合是否包含指定的值
  • boolean isEmpty() 判断集合是否为空
  • int size() 集合的长度,也就是集合中键值对的个数
package map.demo1;

import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) {
        Map<String, String>  m = new HashMap<String, String>();

        m.put("test001", "Bob");
        m.put("test002", "Alice");
        m.put("test003", "Jack");
        m.put("test003", "Test");
        System.out.println(m);

        System.out.println("-----------------");
        System.out.println(m.remove("test001"));
        System.out.println(m.remove("test005"));
        System.out.println(m);

        System.out.println("-----------------");
        System.out.println(m.containsKey("test002"));
        System.out.println(m.containsKey("test005"));

        System.out.println("-----------------");
        System.out.println(m.isEmpty());
        System.out.println(m.size());

        System.out.println("-----------------");
        m.clear();
        System.out.println(m);
    }
}
/**
{test002=Alice, test003=Test, test001=Bob}
-----------------
Bob
null
{test002=Alice, test003=Test}
-----------------
true
false
-----------------
false
2
-----------------
{}
**/

获取功能

  • v get(Object key) 根据键获取值
  • Set keySet() 获取所有键的集合
  • Collection values() 获取所有值的集合
  • Set<Map.Entry<K, V> entrySet() 获取所有键值对对象的集合
package map.demo1;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) {
        Map<String, String> m = new HashMap<String, String>();
        m.put("test001", "Bob");
        m.put("test002", "Alice");
        m.put("test003", "Jack");
        System.out.println(m);

        System.out.println("-----------------");
        System.out.println(m.get("test001"));

        System.out.println("-----------------");
        Set<String> keySet = m.keySet();
        for (String key : keySet){
            System.out.println(key);
        }

        System.out.println("-----------------");
        Collection<String> values = m.values();
        for (String v : values){
            System.out.println(v);
        }
    }
}

遍历

package map.demo1;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test3 {
    public static void main(String[] args) {
        Map<String, String> m = new HashMap<String, String>();
        m.put("test001", "Bob");
        m.put("test002", "Alice");
        m.put("test003", "Jack");

        //keySet()
        Set<String> keySet = m.keySet();
        for (String key : keySet){
            String value = m.get(key);
            System.out.println(key + ',' + value);
        }

        //entrySet()
        Set<Map.Entry<String, String>> entrySet = m.entrySet();
        for (Map.Entry<String, String> me : entrySet){
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "," + value);
        }
    }
}
Collections

collections类是针对集合操作的工具类
Collections类的常用方法

  • public static <T extends Comparable<? super T>> void sort (List<T> list) 将指定的列表按升序排序
  • public static void reverse (list<?> list) 反转指定列表中元素的顺序
  • public static void shuffle (List<?> list) 使用默认的随机源随机排列指定的列表
package map.demo2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();

        list.add(30);
        list.add(10);
        list.add(20);
        System.out.println(list);

        Collections.reverse(list);
        System.out.println(list);

        Collections.sort(list);
        System.out.println(list);

        Collections.shuffle(list);
        System.out.println(list);
    }
}
/**
[30, 10, 20]
[20, 10, 30]
[10, 20, 30]
[20, 10, 30]
**/
Test

需求:通过程序实现斗地主过程中的洗牌,发牌和看牌。要求:对牌进行排序

package map.poker;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

public class Poker {
    public static void main(String[] args) {
        //生成牌
        HashMap<Integer, String> map= new HashMap<Integer, String>();
        ArrayList<Integer> array = new ArrayList<Integer>();

        String[] colors = {"♦","♣","♥","♠"};
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};

        int index=0;
        for (String number : numbers){
            for (String color  : colors){
                map.put(index, color+number);
                array.add(index);
                index++;
            }
        }
        map.put(index, "小王");
        array.add(index);
        index++;
        map.put(index, "大王");
        array.add(index);

        Collections.shuffle(array);
        //System.out.println(map);

        //发牌
        TreeSet<Integer> player01 = new TreeSet<Integer>();
        TreeSet<Integer> player02 = new TreeSet<Integer>();
        TreeSet<Integer> player03 = new TreeSet<Integer>();
        TreeSet<Integer> domain = new TreeSet<Integer>();

        for (int i = 0; i < array.size(); i++) {
            int x = array.get(i);
            if(i >= array.size()-3){
                domain.add(x);
            } else if(i%3==0){
                player01.add(x);
            } else if(i%3==1){
                player02.add(x);
            } else{
                player03.add(x);
            }
        }

        //看牌
        lookPoker("player001", player01, map);
        lookPoker("player002", player02, map);
        lookPoker("player003", player03, map);
        lookPoker("domain", domain, map);
    }
    //看牌
    public static void lookPoker(String name, TreeSet<Integer> ts ,HashMap<Integer, String> hm){
        System.out.println(name+ "的牌:");
        for (Integer key : ts){
            String poker = hm.get(key);
            System.out.print(poker+" ");
        }
        System.out.println();
    }
}
player001的牌:
♥3 ♣4 ♦5 ♥5 ♣6 ♦8 ♣8 ♥8 ♦10 ♠10 ♣J ♥Q ♦K ♥A ♥2 小王 大王 
player002的牌:
♦3 ♠5 ♦6 ♥6 ♠6 ♦7 ♥7 ♠7 ♠8 ♦9 ♣9 ♠9 ♥J ♦Q ♥K ♦A ♣2 
player003的牌:
♣3 ♠3 ♦4 ♥4 ♠4 ♣5 ♥9 ♣10 ♥10 ♦J ♠J ♠Q ♣K ♣A ♠A ♦2 ♠2 
domain的牌:
♣7 ♣Q ♠K