15.collection

数据结构

常见数据结构:栈

数据进入栈的过程称为: 压/进栈

数据离开栈的过程称为: 弹/出栈

栈底和栈顶元素, 栈是一种先进后出的数据结构image-20201015115855692

常见数据结构:队列

数据从后端进入队列过程称为: 入队列

数据从前端离开队列过程称为: 出队列

队列是一种先进先出的模型

image-20201015115930362

常见数据结构: 数组

image-20201015120054990

查询数据通过索引定位, 查询任意数据耗时相同, 插叙效率高

删除数据时, 要将原始数据删除, 同时后面每个数据迁移, 删除效率低

添加元素时, 添加位置后的每个数据后移, 在添加元素, 添加效率极低.

数组是一种查询快, 增删慢的模型

常见数据结构: 链表

image-20201015120344868

链表存储过程

image-20201015120447523

添加元素过程

image-20201015120543688

删除元素过程

image-20201015120617399

查询过程

无论是查询哪一个数据,都需要从head开始遍历

链表是一种增删快,查询慢的数据结构

常见数据结构: 哈希表

哈希表

  • JDK8之后, 底层采用数组+链表实现, 可以说是一个元素为链表的数组
  • JDK8之后, 长度比较长的时候, 底层实现了优化

image-20201015171737064

collection

集合类体系结构

特点: 提供一种存储空间可变的存储类型, 存储的数据内容可以随时发生改变

集合:

  • Collection: 单列集合*
    • List: 可重复*
      • ArrayList
      • LinkedList
    • Set: 不可重复*
      • HashSet
      • TreeSet
  • Map: 双列集合*
    • HashMap

注:其中带*的是接口, 不带的是实现类

概述

collection集合

  • 单列集合的顶层接口, 表示一组对象, 这些对象也称为Collection的元素
  • JDK不提供此接口的任何直接实现类, 他提供更具体的子接口(如Set和List)实现

创建Collection集合的对象

  • 多态的方式
  • 具体的实现类ArrayList

常用方法

方法名 说明
Boolean add(E e) 添加元素
boolean remove(Object) 从集合中移除指定的元素
void clear() 清空集合中的元素
Boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度, 也就是集合中元素的个数

demo

package commonApi.collection;

import java.util.ArrayList;
import java.util.Collection;
/*
    Alt+7打开窗口查看类的方法
 */

public class CollectionDemo2 {
    public static void main(String[] args) {
        // 创建对象
        Collection<String> c = new ArrayList<String>();

        // boolean add(E e): 添加元素
        System.out.println(c.add("hello"));
        System.out.println(c.add("world"));
        System.out.println(c.add("world"));

        // boolean remove(E e): 移除元素
        // System.out.println(c.remove("java"));
        // System.out.println(c.remove("hello"));
        
        // void clear(): 清空集合元素
        // c.clear();
        
        // boolean contains(Object o): 判断集合中是否存在指定元素
        System.out.println(c.contains("java"));
        System.out.println(c.contains("javaee"));
        
        // boolean isEmpty(): 判断集合是否为空
        System.out.println(c.isEmpty());
        
        // int size(): 集合的长度
        System.out.println(c.size());

        System.out.println(c);
    }
}

集合的遍历

Iterator: 迭代器, 集合的专用遍历方式

  • Iterator<E> iterator(): 返回此集合中元素的迭代器, 通过集合的iterator()方法得到
  • 迭代器是通过集合的iterator()方法得到的, 所以我们说它是依赖于集合而存在的

Iterator中的常用方法:

  • E next(): 返回迭代器中的下一个元素
  • boolean hashNext(): 如果迭代器具有更多元素, 则返会true

demo

package commonApi.collection;

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

public class IteratorDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Collection<String> c = new ArrayList<String>();

        // 添加元素
        c.add("hello");
        c.add("world");
        c.add("java");

        // Iterator<E> iterator(): 返回集合元素中的迭代器
        Iterator<String> it = c.iterator();
        /*
            public Iterator<E> iterator() {
                return new Itr();
            }

            private class Itr implements Iterator<E> {
                ...
            }
         */
        // E next(): 返回迭代中的下一元素
        /*
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());  // NoSuchElementException
        System.out.println(it.next());

         */

        // boolean hasNext(); 如果迭代中有元素, 返回true
        /*
        if (it.hasNext()) {
            System.out.println(it.next());
        }
        if (it.hasNext()) {
            System.out.println(it.next());
        }
        if (it.hasNext()) {
            System.out.println(it.next());
        }
        if (it.hasNext()) {
            System.out.println(it.next());
        }

         */

        // 用while循环改进判断
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

List

list概述

有序集合(也称为序列), 用户可以精确控制列表汇总每个元素的插入位置. 用户可以通过整数索引访问元素, 并搜索列表中的元素.

与set集合不同, 列表通常允许重复的元素

ListDemo

package commonApi.list;


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

public class ListDemo {
    public static void main(String[] args) {
        // 创建集合对象
        List<String> list = new ArrayList<String>();

        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        list.add("hello");

        // 输出集合对象
        System.out.println(list);

        // 迭代器方式遍历
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(it.next());
        }

    }
}

常用方法

方法名 说明
void add(int index, E element) 在此集合指定位置插入指定元素
E remove(int index) 删除指定索引出的元素, 返回被删除的元素
E set(int index, E element) 修改指定索引出的元素, 返回被修改的元素
E get(int index) 返回指定所引处的元素

ListDemo

package commonApi.list;

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

public class ListDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        List<String> list = new ArrayList<String>();

        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        // void add(int index, E element)
        list.add(1,"javaee");
        // list.add(11,"javaee");  // IndexOutOfBoundsException

        // E remove(int index)
        list.remove(1);
        // list.remove(11);  // IndexOutOfBoundsException

        // E set(int index, E element)
        System.out.println(list.set(1, "python"));
        // System.out.println(list.set(11, "python"));  // IndexOutOfBoundsException

        // E get(int index)
        System.out.println(list.get(1));
        // System.out.println(list.get(11));  // IndexOutOfBoundsException

        // 输出集合对象
        System.out.println(list);

        // 遍历集合
        for (int i=0; i<list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

List并发修改

并发修改异常: ConcurrentModificationException

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

解决: 用for循环, 然后用集合对象做对应的操作即可

源码分析

public interface List<E> {
 Iterator<E> iterator();
 boolean add(E e);
}
public abstract class AbstractList<E> {
 protected int modCount = 0;
}

public class ArrayList<E> extends AbstractList<E> implements List<E>{
 public Iterator<E> iterator() {
     return new Itr();
 }
 private class Itr implements Iterator<E> {
     int expectedModCount = modCount;
     /*
         modCount: 实际修改集合的次数
         expectModCount: 预期修改集合的次数
     */

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

     public E next() {
         checkForComodification();
         int i = cursor;
         if (i >= size)
             throw new NoSuchElementException();
         Object[] elementData = ArrayList.this.elementData;
         if (i >= elementData.length)
             throw new ConcurrentModificationException();
         cursor = i + 1;
         return (E) elementData[lastRet = i];
     }

     final void checkForComodification() {
         if (modCount != expectedModCount)
             throw new ConcurrentModificationException();
     }
 }
}

示例

package commonApi.list;

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

/*
    ConcurrentModificationException: 当不允许这样的修改时, 可以通过检车到对象的并发修改的方法来抛出此异常
 */

public class ConcurrenceModify {
    public static void main(String[] args) {
        // 创建list集合
        List<String> list = new ArrayList<String>();

        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        // 遍历集合, 得到每个元素, 如果有"world"元素, 添加一个"javaee"
        Iterator<String> it = list.iterator();
        /*
        while (it.hasNext()) {
            String s = it.next();  // ConcurrentModificationException
            if (s.equals("world")) {
                list.add("javaee");
                // 迭代器遍历时, 迭代器元素个数发生了改变, 类似python中遍历时修改列表大小,也会报错
            }
        }
         */

        for (int i=0; i<list.size(); i++) {
            String s = list.get(i);
            if (s.equals("world")) {
                list.add("javaee");
            }
        }
        System.out.println(list);
    }
}

ListIterator

列表迭代器

  • 通过List集合的iterator()方法得到, 所以说它是List集合特有的迭代器

  • 用于允许沿任意方向遍历列表的列表迭代器, 在迭代期间修改列表, 并获取列表中迭代器的当前位置

ListIterator中的常用方法

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

demo

package commonApi.list;

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

public class ListIteratorDemo {
    public static void main(String[] args) {
        // 创建集合对象
        List<String> list = new ArrayList<String>();

        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        // 通过list集合的listIterator()方法得到
        ListIterator<String> lit = list.listIterator();

        while(lit.hasNext()) {
            String s = lit.next();
            System.out.println(s);
        }
        System.out.println("----------");

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

        // 获取列表迭代器
        while(lit.hasNext()) {
            String s = lit.next();
            if (s.equals("world")) {
                lit.add("javaee");
            }
        }

        System.out.println(list);
    }
}

ListIterator不会并发异常源码分析

public interface List<E> {
 Iterator<E> iterator();
 ListIterator<E> listIterator();
}
public abstract class AbstractList<E> {
 protected int modCount = 0;
}

public class ArrayList<E> extends AbstractList<E> implements List<E> {

 public Iterator<E> iterator() {
     return new Itr();
 }
 private class Itr implements Iterator<E> {
     ...
 }
 }
 public ListIterator<E> listIterator() {
     return new ListItr(0);
 }

 private class ListItr extends Itr implements ListIterator<E> {
     public void add(E e) {
         checkForComodification();

         try {
             int i = cursor;
             ArrayList.this.add(i, e);
             cursor = i + 1;
             lastRet = -1;
             expectedModCount = modCount;  // 修改后会把实际修改值赋值给预期修改值, 所以不会触发异常
         } catch (IndexOutOfBoundsException ex) {
             throw new ConcurrentModificationException();
         }
     }
 }
}

增强for循环

增强for: 简化数组和Collection集合的遍历

  • 实现Iterable接口的类允许其对象成为增强型for语句的目标
  • JDK5后出现, 内部原理是一个Iterator迭代器

格式:

for(元素数据类型 变量名: 数组或者Collection集合) {
	// 在此使用变量即可, 该变量就是元素
}

范例

int [] arr = {1, 2, 3};
for(int i: arr) {
	System.out.println(i);
}

Demo

package commonApi.list;

import modifier.pkgs.Parent;

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

public class EnforceForDemo {
    public static void main(String[] args) {
        int [] arr = {1, 2, 3, 4, 5};
        for (int i: arr) {
            System.out.println(i);
        }

        System.out.println("--------");

        String [] strArray = {"hello", "world", "java"};
        for (String s: strArray) {
            System.out.println(s);
        }
        System.out.println("--------");

        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");

        for (String s: list) {
            System.out.println(s);
        }
        
        // 内部是一个迭代器
        for(String s: list) {
            if (s.equals("world")) {
                list.add("javaee");  // ConcurrentModificationException, 说明内部原理还是一个迭代器
            }
        }
    }
}

List集合子类特点

常用List子类: ArrayList, LinkedList

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

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

Demo

package commonApi.list;

import java.util.ArrayList;
import java.util.LinkedList;

public class LinkedAndArrayList {
    public static void main(String[] args) {
        // 创建
        ArrayList<String> array = new ArrayList<String>();

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

        // 遍历
        for (String s: array) {
            System.out.println(s);
        }

        System.out.println("----------");

        LinkedList<String> linkedList = new LinkedList<String>();
        linkedList.add("hello");
        linkedList.add("world");
        linkedList.add("java");
        
        for(String s: linkedList) {
            System.out.println(s);
        }
    }
}

LinkedList集合

特有功能

方法名 说明
public void addFirst(E e) 在改列表开头插入指定元素
public void addLast(E e) 将指定的元素追加到此列表末尾
public E getFirst() 获取列表中第一个元素
public E getLast() 获取列表中最后一个元素
public E removeFirst() 移除并返回列表第一个元素
public E removeLast() 移除并返回列表最后一个元素

Demo

package commonApi.list;

import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        // 创建LinkedList对象
        LinkedList<String> linkedList = new LinkedList<String>();
        linkedList.add("hello");
        linkedList.add("world");
        linkedList.add("java");

        // public void addFirst(E e)
        linkedList.addFirst("javaee");
        // public void addLast(E e)
        linkedList.addLast("javaee");
        //public E getFirst() 获取列表中第一个元素
        System.out.println(linkedList.getFirst());
        // public E getLast() 获取列表中最后一个元素
        System.out.println(linkedList.getLast());
        // public E removeFirst() 移除并返回列表第一个元素
        System.out.println(linkedList.removeFirst());
        // public E removeLast() 移除并返回列表最后一个元素
        System.out.println(linkedList.removeLast());
        System.out.println(linkedList);
    }
}

Set集合

set集合特点:

  • 不包含重复元素的集合
  • 没有带索引的方法, 所以不能使用普通for循环遍历

Demo

package commonApi.list;

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

public class SetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Set<String> st = new HashSet<String>();

        // 添加元素
        st.add("hello");
        st.add("world");
        st.add("java");
        // st.add("java");  // Set不能包含重复元素

        // 遍历
        for(String s: st) {
            System.out.println(s);
        }
    }
}

哈希值

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

Object类中有一个方法可以获取对象的哈希值

  • public int hashCode(): 返回对象的哈希码值

哈希值的特点:

  • 同一个对象调用多次hashCode方法返回的哈希值是相同的
  • 默认情况下, 不同对象的哈希值是不同的. 重写hashCode()方法, 可以实现让不同对象的哈希值相同.

Demo

package commonApi.hash;

public class HashCodeDemo {
    public static void main(String[] args) {
        // 创建学生对象
        Student s1 = new Student("林青霞", 30);

        // 对一个对象多次调用hashCode()方法返回哈希值一样
        System.out.println(s1.hashCode());  // 41903949
        System.out.println(s1.hashCode());  // 41903949

        // 默认情况下, 两个对象使用hashCode方法, 哈希值不一样
        Student s2 = new Student("林青霞", 30);
        System.out.println(s1.hashCode());  // 41903949
        System.out.println(s2.hashCode());  // 488970385

        // 重写hashCode()方法,可以修改hashCode()返回的哈希值

        System.out.println("hello".hashCode());  // 99162322
        System.out.println("world".hashCode());  // 113318802

        // 不同值哈希值一样的情况
        System.out.println("重地".hashCode());  // 1179395
        System.out.println("通话".hashCode());  // 1179395
    }
}

HashSet集合

Set的接口实现类, 特点:

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不做保证, 存储和取出顺序可能不一致
  • 没有带索引的方法, 所以不能用for循环
  • 不包含重复元素

Demo

package set;

import java.util.HashSet;

public class HashSetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        HashSet<String> hs = new HashSet<String>();
        // 添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("java");

        // 遍历
        for(String s: hs) {
            System.out.println(s);
        }
    }
}

add源码分析

image-20201015170424192

public boolean add(E e) {
 return map.put(e, PRESENT)==null;
}

static final int hash(Object key) {
 int h;
 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public V put(K key, V value) {
 return putVal(hash(key), key, value, false, true);
}

// hash值和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
 Node<K,V>[] tab; Node<K,V> p; int n, i;
 // 如果哈希表未初始化, 则对其进行初始化
 if ((tab = table) == null || (n = tab.length) == 0)
     n = (tab = resize()).length;
 // 根据对象的哈希值计算对象的存储位置, 如果该位置没有元素, 就存储元素
 if ((p = tab[i = (n - 1) & hash]) == null)
     tab[i] = newNode(hash, key, value, null);
 else {
     Node<K,V> e; K k;
     /*
         存入的元素和以前的元素比较哈希值
             如果哈希值不同, 会继续向下执行, 把元素添加到集合
             如果哈希值相同, 调用对象的equals()方法
                 如果返回false, 会继续向下执行, 把元素添加到集合
                 如果返回true, 说明元素重复, 不存储
     */
     if (p.hash == hash &&
         ((k = p.key) == key || (key != null && key.equals(k))))
         e = p;
     else if (p instanceof TreeNode)
         e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
     else {
         for (int binCount = 0; ; ++binCount) {
             if ((e = p.next) == null) {
                 p.next = newNode(hash, key, value, null);
                 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                     treeifyBin(tab, hash);
                 break;
             }
             if (e.hash == hash &&
                 ((k = e.key) == key || (key != null && key.equals(k))))
                 break;
             p = e;
         }
     }
     if (e != null) { // existing mapping for key
         V oldValue = e.value;
         if (!onlyIfAbsent || oldValue == null)
             e.value = value;
         afterNodeAccess(e);
         return oldValue;
     }
 }
 ++modCount;
 if (++size > threshold)
     resize();
 afterNodeInsertion(evict);
 return null;
}

LinkedHashSet集合

特点:

  • 哈希表和链表实现的Set接口, 具有可预测的迭代顺序
  • 链表保证元素有序
  • 哈希表保证元素唯一

Demo

package set;

import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();

        // 添加元素
        linkedHashSet.add("hello");
        linkedHashSet.add("world");
        linkedHashSet.add("java");
        linkedHashSet.add("world");  // 不会存储

        // 遍历
        for(String s: linkedHashSet) {
            System.out.println(s);
        }
    }
}

TreeSet集合

特点:

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

Demo

package set;

import java.util.TreeSet;

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

        // 添加元素
        ts.add(10);
        ts.add(50);
        ts.add(30);
        ts.add(40);

        ts.add(50);

        // 遍历几个
        for(Integer i: ts) {
            System.out.println(i);
        }
    }
}

自然排序Comparable的使用

创建学生对象并遍历, 创建TreeSet集合使用无参构造方法

要求: 年龄从小到大排序, 年龄相同, 按照姓名的字母顺序排序

方式一:

Student

package set;

public class Student implements Comparable<Student> {  // 指定泛型
    private String name;
    private int age;

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

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

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

    public int getAge() {
        return this.age;
    }

    @Override
    public int compareTo(Student s) {
        // return 0;  // 会把所有元素当成同一元素, 只会出一个
        // return 1;  // 会认为前者比后者小, 按照存储顺序输出
        // return -1;  // 会认为前者比后者大, 按照存储的倒序输出
        int num =  this.age - s.age;  // 升序
        int num2 = num==0?this.name.compareTo(s.name): num;  // num==0,也就是年龄相同, 则比较姓名字母排序
        return num2;
    }
}

ComparableDemo

package set;

import java.util.TreeSet;

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

        // 创建学生
        Student s1 = new Student("xishi",29);
        Student s2 = new Student("wangzhaojun",28);
        Student s3 = new Student("diaochan",30);
        Student s4 = new Student("yangyuhuan",33);

        Student s5 = new Student("lingqingxia",33);
        Student s6 = new Student("lingqingxia",33);

        // 添加学生到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        // 遍历集合
        for(Student s: ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }

    }
}

方式二:

Student

package set.comparableDemo2;

public class Student {
    private String name;
    private int age;

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

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

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

    public int getAge() {
        return this.age;
    }

}

ComparableDemo

package set.comparableDemo2;

import set.Student;
/*
    在构造方法中使用带参数构造方法实现
 */
import java.util.Comparator;
import java.util.TreeSet;

public class ComparableDemo {
    public static void main(String[] args) {
        // 创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // return 0;
                // this.age - s.age, s1, s2
                int num = s1.getAge() - s2.getAge();
                int num2 = num==0?s1.getName().compareTo(s2.getName()): num;
                return num2;
            }
        });  // 匿名内部类实现

        // 创建学生
        Student s1 = new Student("xishi",29);
        Student s2 = new Student("wangzhaojun",28);
        Student s3 = new Student("diaochan",30);
        Student s4 = new Student("yangyuhuan",33);

        Student s5 = new Student("lingqingxia",33);
        Student s6 = new Student("lingqingxia",33);

        // 添加学生到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        // 遍历集合
        for(Student s: ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }

    }
}

总结:

  • 使用TreeSet集合存储自定义对象, 带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序, 就是让集合构造方法接受Comparator的实现类对象, 重写compareTo(T o1, T o2)方法
  • 重写方法时, 一定要注意排序规则要求的主要条件和次要条件来写

泛型

概述

JDK5后引入的特性, 提供了编译时类型安全监测机制, 该机制允许在编译时检测到非法的类型. 他的本质是参数化类型, 也就是说所有操作的数据类型被指定为一个参数.

参数化类型:

  • 就是讲类型有原来具体的类型参数化, 然后在使用/调用时传入具体的类型
  • 可以用在类, 方法和接口中, 分别被称为泛型类, 泛型方法, 泛型接口

泛型定义格式:

  • <类型>: 指定一种类型格式, 这里的类型可以看成是形参

  • <类型1, 类型2...>: 指定多种类型的格式, 多种类型之间用逗号隔开. 这里的类型可以看成是形参.

  • 将来具体调用的时候给定的类型可以看成是实参, 并且实参的类型只能是引用数据类型.

泛型的好处:

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

Demo

package set;

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

public class GenericDemo {
    public static void main(String[] args) {
        // Collection存储字符, 遍历
        // Collection c = new ArrayList();  // 未使用泛型, 会报错
        Collection<String> c = new ArrayList<String>();

        // 添加元素
        c.add("hello");
        c.add("world");
        c.add("java");
        // c.add(100);  // 1.泛型会在编译中就发现错误

        //遍历集合
        // Iterator it = c.iterator();  // 未使用泛型
        Iterator<String> it = c.iterator();
        while (it.hasNext()) {
            // Object o = it.next();
            // System.out.println(o);
            // String s = (String)it.next();  // ClassCastException: 类型转换错误
            String s = it.next();  // 2.避免使用强制转换
            System.out.println(s);
        }
    }
}

泛型类

定义格式:

  • 格式: 修饰符 class 类型名<类型> {}
  • 范例: pulic class Ceneric<T> {}, 此处T可以随便写为任意标识, 常见的如T,E,K,V等形式的参数常用语表示泛型.

Demo

package set.generic;
/*
    实现一个参数接受多种数据类型, 使用泛型类
 */
public class GenericDemo2 {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("林青霞");
        System.out.println(s.getName());

        Teacher t = new Teacher();
        t.setAge(30);
        System.out.println(t.getAge());

        System.out.println("--------");
        Generic<String> g1 = new Generic<String>();
        g1.setT("林青霞");
        System.out.println(g1.getT());

        Generic<Integer> g2 = new Generic<Integer>();
        g2.setT(30);
        System.out.println(g2.getT());

        Generic<Boolean> g3 = new Generic<Boolean>();
        g3.setT(true);
        System.out.println(g3.getT());
    }
}

泛型方法

泛型方法格式

  • 格式: 修饰符 <类型> 返回值类型 方法名(类型 变量名) {}
  • 范例: public <T> void show(T t) {}

Generic

package set.generic.genericFunction;
/*
// 这种方式实现很麻烦
public class Generic {
    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 Generic<T> {
    public void show(T t) {
        System.out.println(t);
    }
}
 */

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

GenericFunction

package set.generic.genericFunction;

public class GenericFunction {
    public static void main(String[] args) {
        /*
        Generic g = new Generic();
        g.show("林青霞");
        g.show(30);
        g.show(true);
        // g.show(12.34);  // 报错
         */

        // 泛型类的实现
        /*
        Generic<String> g1 = new Generic<String>();
        g1.show("林青霞");

        Generic<Integer> g2 = new Generic<Integer>();
        g2.show(30);

        Generic<Boolean> g3 = new Generic<Boolean>();
        g3.show(true);
         */

        // 泛型方法实现
        Generic g = new Generic();
        g.show("林青霞");
        g.show(30);
        g.show(true);
        g.show(12.34);
    }
}

泛型接口

泛型接口的定义格式

  • 格式: 修饰符 interface 接口名 <类型> {}
  • 范例: public interface Generic <T t> {}

Generic

package set.generic.genericInterface;

public interface Generic<T> {
    void show(T t);
}

GenericImpl

package set.generic.genericInterface;

public class GenericImpl<T> implements Generic<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

GenericDemo

package set.generic.genericInterface;

public class GenericDemo {
    public static void main(String[] args) {
        Generic<String> g1 = new GenericImpl<String>();
        g1.show("林青霞");

        Generic<Integer> g2 = new GenericImpl<Integer>();
        g2.show(30);
    }
}

类型通配符

为表示各种泛型List的父类, 可以使用类型通配符

  • 类型通配符: <?>
  • List<?>: 表示元素类型位置的List, 他的元素可以匹配任何的类型, 但是只能是单一类型
  • 仅表示他是各种泛型List的父类, 并不能把元素添加到其中

如果我们不希望List<?>是任何泛型List的父类, 只希望他代表某一类泛型List的父类, 可以使用类型通配符的上限.

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

除了可以指定类型通配符上限, 也可以表示类型通配符的下线

  • 类型通配符下限: <?super类型>
  • List<?super Number>: 他表示类型是Number或者其父类

GenericDemo

package set.generic.typeGeneric;

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

public class GenericDemo {
    public static void main(String[] args) {
        // 类型通配符 <?>
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();
        System.out.println("--------");

        // 类型通配符上限: <? extends 类型>
        // List<? extends Number> list4 = new ArrayList<Object>();  // 超过了Number的上限
        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>();  // 超过Number下限
    }
}

可变参数

可变参数又称为参数个数可变, 用作方法的形参出现, 那么方法参数个数就是可变的

  • 格式: 修饰符 返回值类型 方法名(数据类型...变量名) {}
  • 范例: public static int sum(int ... a) {}

VariableArgsDemo

package set.generic.variableParams;

public class VariableArgsDemo {
    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));
        System.out.println(sum(10, 20, 30, 40, 50));
        System.out.println(sum(10, 20, 30, 40, 50, 60));

    }
    /*
    public static int sum(int a, int b) {
        return a + b;
    }

    public static int sum(int a, int b, int c) {
        return a + b + c;
    }

    public static int sum(int a, int b, int c, int d) {
        return a + b + c + d;
    }
     */

    public static int sum(int ... a) {
        // System.out.println(a);  // a 是个数组
        int sum = 0;
        for(int i : a) {
            sum += i;
        }
        return sum;
    }

    public static int sum(int a, int ... b) {
        // 多个参数包含可变参数, 可变参数放在最后
        int sum = 0;
        for(int i : b) {
            sum += i;
        }
        return sum;
    }
}

注意:

  • 可变参数的变量是一个数组
  • 如果方法有多个参数,包含可变参数, 可变参数要放在最后

可变参数的使用

Arrays工具类中有一个静态方法

  • public static <T> List <T> asList(T ... a): 返回由指定数组支持的固定大小的列表
  • 不能做增删操作, 可以修改

List接口中有一个静态方法:

  • public static <E> List<E> of(E ... elements): 返回包含任意数量元素的不可变列表
  • 不能做增删改操作

Set接口中有一个静态方法:

  • public static Set of(E ... elements): 返回一个包含任意数量元素的不可变集合
  • 不能给重复的元素
  • 返回的集合不能做增删操作, 没有修改方法

ArgsFunctionDemo

package set.generic.variableParams;

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

public class ArgsFunctionDemo {
    public static void main(String[] args) {
        // public static <T> List <T> asList(T ... a): 返回由指定数组支持的固定大小的列表
        List<String> list = Arrays.asList("hello", "world", "java");
        // list.add("javaee");  // UnsupportedOperationException
        // list.remove("world");  // UnsupportedOperationException
        list.set(1, "javaee");
        System.out.println(list);

        // public static <E> List <E> of(E ... elements): 返回包含任意数量元素的不可变列表
        List<String> list1 = List.of("hello", "world", "java", "world");
        // list1.add("javaee");  // UnsupportedOperationException
        // list1.remove("java");  // UnsupportedOperationException
        // list1.set(1, "javaee");  // UnsupportedOperationException
        System.out.println(list1);

        // public static <E> Set <E> of(E ... elements): 返回一个包含任意数量元素的不可变集合
        // Set<String> list2 = Set.of("hello", "world", "java", "world");  // IllegalArgumentException
        Set<String> list2 = Set.of("hello", "world", "java");
        // list2.add("javaee");  // UnsupportedOperationException
        // list2.remove("java");  // UnsupportedOperationException
        System.out.println(list2);
    }
}

Map集合

Map集合和概述

k-v类型数据结构

  • Interface Map<K, V>: K-键的类型, V-值的类型
  • 将键映射到值的对象; 不能包含重复的键; 每一个键可以映射到最多一个值
  • 举例: 学生的学号和姓名

Demo

package map.hashMap;

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

public class MapDemo {
    public static void main(String[] args) {
        // 创建集合对面
        Map<String, String> map = new HashMap<String, String>();

        // V put(K key, V value) 将指定的值与该映射中的指定键相关联
        map.put("oldboy01", "林青霞");
        map.put("oldboy02", "张曼玉");
        map.put("oldboy03", "王祖贤");
        map.put("oldboy03", "刘岩");

        // 输出
        System.out.println(map);
        // {oldboy02=张曼玉, oldboy01=林青霞, oldboy03=刘岩}
    }
}

注意:

  • 键唯一

Map集合的基本方法

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

Demo

package map.hashMap;

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

public class MapFunction {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<>();

        // V put(K key, V value): 添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        // V remove(Object Key): 根据键删除键值对元素
        System.out.println(map.remove("郭靖"));
        System.out.println(map.remove("郭镶"));

        // void clear(): 移除所有的键值对元素
        // map.clear();

        // boolean containsKey(Object Key): 判断集合是否包含指定的键
        System.out.println(map.containsKey("郭靖"));
        System.out.println(map.containsKey("郭镶"));

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

        // int size(): 集合的长度, 也就是集合中键值对的个数
        System.out.println(map.size());

        // 输出集合
        System.out.println(map);
    }
}

Map集合的过去方法

方法名 说明
V get(Object key) 根据键获取值
Set keySet() 获取所有键的集合
Collection values() 获取所有值的集合
Set<Map.Entry<K, V>> entrySet() 获取所有键值对对象的集合

Demo

package map.hashMap;

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

public class MapGetFunction {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<>();

        // V put(K key, V value): 添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        // V get(Object key): 根据键获取值
        System.out.println(map.get("张无忌"));
        System.out.println(map.get("张三丰"));

        // Set<K> KeySet(): 获取所有键的集合
        System.out.println(map.keySet());
        for(String s: map.keySet()) {
            System.out.println(s);
        }

        // Collection <V> values(): 获取所有值的集合
        System.out.println(map.values());

    }
}

Map集合的遍历

方法一:

  • 获取所有键的集合, keySet
  • 遍历所有的键, 获取每一个键, 增强for
  • 根据键去找值, 用get(Object key)方法

Demo

package map.hashMap;

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

public class MapTravel {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<>();

        // V put(K key, V value): 添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

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

方式二:

  • 获取每一个键值对对象的集合, Set<Map.Entry<K, V>> entrySet()
  • 遍历对象集合, 获取每一个键值对, 增强for遍历 map.entry
  • 根据键值对获取键和值
    • getKey()得到键
    • getValue()得到值

Demo

package map.hashMap;

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

public class MapTravel {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<>();

        // V put(K key, V value): 添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");
        // 方式一:
        /*
        Set<String> keySet = map.keySet();
        for(String key: keySet) {
            String value = map.get(key);
            System.out.println(key + "," + value);
        }
         */

        // 方式二:
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for(Map.Entry<String, String> entry: entries) {
            System.out.println(entry.getKey() + "," + entry.getValue());
        }
    }
}

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): 使用默认的随机源随机排序指定的列表

Demo

package collections;

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

public class CollectionsDemo {
    public static void main(String[] args) {
        // 创建集合对象
        List<Integer> list = new ArrayList<Integer>();

        // 添加元素
        list.add(30);
        list.add(20);
        list.add(50);
        list.add(10);
        list.add(40);

        // Public static <T extends Comparable<? Super T>> void sort(List < T > list): 将指定的列表按照升序排序
        Collections.sort(list);

        // public static void reverse(List < ? > list): 反转指定列表中元素的顺序
        Collections.reverse(list);

        // public static void shuffle(List < ? > list): 使用默认的随机源随机排序指定的列表
        Collections.shuffle(list);

        System.out.println(list);
    }
}

斗地主扑克牌案例

package collections.poker;

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

public class PokerDemo {
    public static void main(String[] args) {
        // 创建一个牌盒, 存储牌
        ArrayList<String> array = new ArrayList<String>();

        // 往牌盒里面装牌
        /*
            ♦2, ♦3,...,♦A
            ♣2, ♣3,...,♣A
            ♥2, ♥3,...,♥A
            ♠2, ♠3,...,♠A
            大王, 小王
         */
        // 定义花色组
        String[] colors = {"♦", "♣", "♥", "♠"};
        // 定义点数
        String[] numbers = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
        for(String color: colors) {
            for(String number: numbers) {
                array.add(color + number);
            }
        }
        array.add("大王");
        array.add("小王");

        // 洗牌
        Collections.shuffle(array);

        // 发牌
        ArrayList<String> array1 = new ArrayList<String>();
        ArrayList<String> array2 = new ArrayList<String>();
        ArrayList<String> array3 = new ArrayList<String>();
        ArrayList<String> array4 = new ArrayList<String>();  // 底牌

        for(int i=0; i<array.size(); i++){
            String poker = array.get(i);
            if (i>=array.size()-3) {
                array4.add(poker);
            } else if(i%3==0){
                array1.add(poker);
            } else if(i%3==1){
                array2.add(poker);
            } else if(i%3==2){
                array3.add(poker);
            }
        }

        // 看牌
        lookPoker("array1", array1);
        lookPoker("array2", array2);
        lookPoker("array3", array3);
        lookPoker("array4", array4);
    }

    // 看牌方法
    public static void lookPoker(String name, ArrayList<String> array) {
        System.out.print(name + "手牌是:");
        for(String poker: array) {
            System.out.print(poker + " ");
        }
        System.out.println();
    }
}

原文地址:https://www.cnblogs.com/ryxiong-blog/p/13890435.html