15.collection
数据结构
常见数据结构:栈
数据进入栈的过程称为: 压/进栈
数据离开栈的过程称为: 弹/出栈
栈底和栈顶元素, 栈是一种先进后出的数据结构
常见数据结构:队列
数据从后端进入队列过程称为: 入队列
数据从前端离开队列过程称为: 出队列
队列是一种先进先出的模型
常见数据结构: 数组
查询数据通过索引定位, 查询任意数据耗时相同, 插叙效率高
删除数据时, 要将原始数据删除, 同时后面每个数据迁移, 删除效率低
添加元素时, 添加位置后的每个数据后移, 在添加元素, 添加效率极低.
数组是一种查询快, 增删慢的模型
常见数据结构: 链表
链表存储过程
添加元素过程
删除元素过程
查询过程
无论是查询哪一个数据,都需要从head开始遍历
链表是一种增删快,查询慢的数据结构
常见数据结构: 哈希表
哈希表
- JDK8之后, 底层采用数组+链表实现, 可以说是一个元素为链表的数组
- JDK8之后, 长度比较长的时候, 底层实现了优化
collection
集合类体系结构
特点: 提供一种存储空间可变的存储类型, 存储的数据内容可以随时发生改变
集合:
- Collection: 单列集合*
- List: 可重复*
- ArrayList
- LinkedList
- Set: 不可重复*
- HashSet
- TreeSet
- List: 可重复*
- 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源码分析
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 |
获取所有键的集合 |
Collection |
获取所有值的集合 |
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();
}
}