集合、泛型
集合和数组都是容器,那么他们的区别是什么呢?
数组长度是固定的,集合的长度是可变的。
数组中只能存放同一元素,只可以存放基本数据类型。集合的存储是对象,而且对象的类型可以不一致。
按照存储结构来分,集合可以分为2大类,分别是:
单列集合Collection
双列集合Map
他俩同属于java.util包
Collection集合:
单列集合的根接口,用于存储一系列符合某种规则的元素,他有两个重要的子接口,分别是List和set
List特点:
有序,元素可以重复,有索引
主要有:ArrayList和LinkList
set特点:
元素无序,而且元素不可以重复。
主要有:HashSet和TreeSet
Collection常用功能:
package com.rongyu.day10;
import java.util.ArrayList;
import java.util.Collection;
/**
* 测试Collocation
*/
public class Dome01 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
//add
collection.add("张三");
collection.add("李四");
collection.add("王五");
System.out.println(collection);
//contains 判断集合中是否有该元素 返回布尔值
System.out.println(collection.contains("张三"));
//remove删除集合中的元素 返回布尔值
System.out.println(collection.remove("张三"));
System.out.println(collection);
//size
System.out.println(collection.size());
//toArray() 转换为一个Object数组
Object[] objects = collection.toArray();
for (Object object : objects) {
System.out.println(object);
}
//clear 清空集合内所有元素
collection.clear();
System.out.println(collection);
// isEmpty() 判断集合是否为空 返回布尔值(为空返回true)
System.out.println(collection.isEmpty());
}
}
Iterator迭代器:
用于遍历集合中的所有元素,但是他与collocation接口和Map接口有所不同,Collection和Map主要用于存储元素,但是Iterator主要用于迭代访问。
迭代器的主要方法:
next()返回迭代的下一个元素
hasNext()看还有元素需要迭代不,有的话返回true
package com.rongyu.day10;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* 测试 Iterator 迭代器
*/
public class Dome02 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
//add
collection.add("张三");
collection.add("李四");
collection.add("王五");
//Iterator
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
}
}
注意如果没有判断就进行next操作会报出java.util.NoSuchElementException
迭代器的实现原理:
他在遍历集合时,内部采用指针的方式来跟踪集合中的元素。开始时,他会现在迭代器的第一个元素之前,每次调用一次next方法,指针就会向后移动一位,指向第一个元素并将该元素返回,再次调用也是如此,知道hasnext为false。
增强for循环:
package com.rongyu.day10;
import java.util.ArrayList;
import java.util.Collection;
/**
* 增强for循环
*/
public class Dome03 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
//add
collection.add("张三");
collection.add("李四");
collection.add("王五");
for (String s : collection) {
System.out.println(s);
}
}
}
他的内部原理也是iterator迭代器,所以在迭代过程中不能对集合的元素进行增删操作。
泛型:
就是在类中或者方法中预支的使用未知类型。默认为Object类型。
泛型的优点:
将运行时期的ClassCastException,转译成了编译失败;
避免了强制转型的麻烦;
泛型的定义与使用:
类:
package com.rongyu.day10;
/**
* 定义泛型类
*/
public class Dome04<E> {
private E e;
public void setE(E e){
this.e = e;
}
public E getE(){
return e;
}
}
package com.rongyu.day10;
/**
* 测试泛型类
*/
public class TestE {
public static void main(String[] args) {
Dome04<String> dome04 = new Dome04<>();
dome04.setE("张三");
System.out.println(dome04.getE());
Dome04<Integer> dome041 = new Dome04<>();
dome041.setE(12);
System.out.println(dome041.getE());
}
}
方法:
package com.rongyu.day10;
/**
* 测试泛型的方法
*/
public class Dome05 {
public static void main(String[] args) {
Dome05 dome05 = new Dome05();
dome05.show(12);
String zhangSan = dome05.show2("ZhangSan");
System.out.println(zhangSan);
}
//定义泛型方法
public <E> void show(E e){
System.out.println(e.getClass());
}
public <E> E show2(E e){
return e;
}
}
含有泛型的接口:
跟类相似;
可以在实现接口时明确类型;
也可以一直不确定类型,直到创建对象的时候确定类型;
泛型通配符:
当时用泛型类或者接口时,泛型的类型不确定的时候可以通过通配符<?>表示;
注意:泛型不存在继承关系;
泛型上限:
格式:类型名称<? extends 类> 对象名称
意义:只能接收该类型和他的子类;
泛型下限:
格式:类型名称 <? super 类>对象名称
意义:只能接收该类和他的父类型
集合综合案列:(斗地主)
package com.rongyu.day10;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 集合实现斗地主
*/
public class Dome06 {
public static void main(String[] args) {
//准备牌
List<String> pai = new ArrayList<>();
//花色
List<String> huase = new ArrayList<>();
//数字
List<String> shuzi = new ArrayList<>();
//添加花色
huase.add("♣");
huase.add("❤");
huase.add("♦");
huase.add("♣");
for (int i = 2; i <= 10; i++) {
shuzi.add(i+"");
}
shuzi.add("A");
shuzi.add("J");
shuzi.add("Q");
shuzi.add("K");
for (String h : huase) {
for (String s : shuzi) {
String card = h + s;
pai.add(card);
}
}
pai.add("大王");
pai.add("小王");
Collections.shuffle(pai);
//创建3个玩家
List<String> player01 = new ArrayList<>();
List<String> player02 = new ArrayList<>();
List<String> player03 = new ArrayList<>();
List<String> dipai = new ArrayList<>();
for (int j = 0; j < pai.size(); j++) {
String pai1 = pai.get(j);
if (j>=51){
dipai.add(pai1);
}else {
if (j%3 == 0){
player01.add(pai1);
}else if (j%3 == 1){
player02.add(pai1);
}else {
player03.add(pai1);
}
}
}
//打印
System.out.println("玩家一:"+player01);
System.out.println("玩家二:"+player02);
System.out.println("玩家三:"+player03);
System.out.println("底牌:"+dipai);
}
}
List集合的特点:
1.他是一个存储元素有序的集合。添加时候是什么顺序,存储到集合里面就是什么顺序的。
2.他是一个带索引的集合,通过索引就可以精准的操作集合中的元素。
3.集合中可以存在重复的元素,通过元素的equals方法,来比较是否有相同的元素。
List中常用的几个方法:
get
set
add
remove
List的子类:
ArrayList集合:
存储的底层数数组,所以他的特点是查找快,删除和增加很慢。
LinkedList集合:
底层是链表结构,所以他的添加和删除快。
他提供了大量首尾操作的例子;
package com.rongyu.day10;
import java.util.LinkedList;
/**
* 测试 LinkedList 集合的首尾操作
*/
public class Dome07 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
//添加到第一个元素
list.addFirst("a");
list.addFirst("b");
list.addFirst("c");
System.out.println(list);
//获取元素
System.out.println(list.getFirst());
System.out.println(list.getLast());
//删除元素 返回值是该元素
System.out.println(list.removeFirst());
System.out.println(list.removeLast());
System.out.println(list);
while (!list.isEmpty()){
System.out.println(list.pop());//弹出栈顶元素
}
}
}
Set集合:
HashSet集合:
它存储的元素都是不可重复的,并且他是无序的(存储的顺序不一样),底层是由HashMap支持。他根据对象的哈希值来哈希值来确定元素在集合中的存储位置,因此具有很好的存储和查询功能。其保证唯一的方式依赖于:hashcode 和equals 方法
HashSet的存储结构(哈希表):
什么事哈希表:
jdk1.8之前,哈希表底层采用数组加链表实现的,就是使用链表处理冲突,同一hash值的链表都存储在一个链表里,但是当hash值相等的元素过多时,key值得查找速率就会过慢,而在jdk1.8之后,采用数组加链表加红黑树的存储实现,当链表长度超过阈值8时,将链表转化为红黑树,大大减少了查询的时间。
通过对一个数字进行模运算,获得一组下标,存储数据,当产生哈希冲突时,也就是下标一样的情况,会用链表来指针指向他的后一个值。
HashSet存储元素:
package com.rongyu.day10;
import java.util.Objects;
/**
* 创建学生类
*/
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.rongyu.day10;
import java.util.HashSet;
/**
* 测试 hashSet
*/
public class Dome08 {
public static void main(String[] args) {
HashSet<Student> hashSet = new HashSet<>();
Student student1 = new Student("张三",1);
Student student2 = new Student("李四",2);
Student student3 = new Student("王五",3);
Student student4 = new Student("小明",4);
Student student5 = new Student("小红",5);
hashSet.add(student1);
hashSet.add(student2);
hashSet.add(student3);
hashSet.add(student4);
hashSet.add(student5);
System.out.println(hashSet);
Student student6 = new Student("张三",1);
hashSet.add(student6);
System.out.println(hashSet);
}
}
总结:
equals默认比较的是地址是否相等,所以需要给student重写equals和hashcode方法才可以去除重复的数据。
LinkedHashSet方法:
是一个有序的。
可变参数:
举例:public void show(int… pram):表示的是他的参数是可以是多个没有的。
Collections:
他是一个集合工具类,用来对集合进行操作。
addAll() 往集合中添加一些元素 返回布尔值
shuffle() 打乱集合的顺序
sort() 将集合进行默认排序
sort(list,Comparator<? super T>);将集合中的数据按照指定的规则排序。
Comparator比较器:
package com.rongyu.day10;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 测试Comparator
*/
public class Dome09 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("abc");
list.add("dsd");
list.add("fgd");
list.add("gfg");
list.add("hjh");
Collections.sort(list);
System.out.println(list);
System.out.println("------------------------------");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) - o1.charAt(0);
}
});
System.out.println(list);
}
}
简述Comparable和Comparator两个接口的区别:
**Comparable:**强行对实现他的每个类进行排序,称为类的自然排序,类的
compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码
实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进
行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
**comparator:**强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(Collections.sort
或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
**注意:**要对集合中的元素完成排序,就必须实现Compeable接口。