集合Collection
集合体系结构
单列集合(Collection)(接口)
创建对象使用多态的方法例如:
Collection<String> strArr= new ArrayList<String>();
- Collection集合常用方法
boolean add(E e) //添加元素
boolean remove(Object o) //从集合中移除指定元素
void clear() //清空集合中元素
boolean contains(Object o)//判断集合中是否存在指定元素
boolean isEmpty() //判断集合是否为空
int size()//返回集合长度
- Collection集合遍历
- Iterator:迭代器,集合的专用遍历方式
- Iterator iterator(); 返回此集合中元素的迭代器,通过集合的iterator()方法得到迭代器
- 常用方法
- E next();返回迭代中的下一个元素
- boolean has Next(); 如果迭代具有更多元素则返回true
- 遍历集合案例
public static void main(String[] args) {
Collection<String> strArr= new ArrayList<String>();
strArr.add("asd");
strArr.add("qwe");
Iterator<String> it = strArr.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println("s = " + s);
}
}
结果:
s = asd
s = qwe
List(可重复)(接口)
- List集合概述和特点
- 继承自Collection接口
- 允许重复元素
- 存在索引
- 有序 :存储和取出的元素顺序一致
- List集合特有方法
void add(int index,E element) //在此集合中指定位置插入元素
E remove (int index) //根据索引删除元素并返回
E set(int index,E element)//修改索引处元素并返回
E get(int index) //返回索引处元素
- 并发修改异常 ConcurrentModificationException
- 产生原因 迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成迭代器中预期修改值和实际修改值不一样
- 解决方案一 使用for循环遍历
- ListIterator
- 通过List集合的listiterator()方法得到,所以说它是List集合特有的迭代器
- ListIterator常用方法
void add(E e) //将指定元素插入列表 //
boolean hasNext() //判断下一个元素是否存在
boolean hasPrevious() //判断上一个元素是否存在
E next()//返回列表中下一个元素,移动光标
E privious()//返回列表中上一个元素,移动光标
PS:ListIterator的add方法会将实际修改值赋值给预期修改值,所以不会出现并发修改异常
- 增强for循环
- 增强for:简化数组和Collection集合遍历
- 实现Iterable接口的类允许其对象成为增强型for语句的目标
- 它是JDK5之后出现的,其内部原理是一个**Iterator迭代器 **
- 增强for的格式:
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
} - 示例
int[] arr = {1,2,3,4,5}
for(int i :arr){
System.out.println(i);
}
List<String> list = new ArrayList<String>();
lsit.add("asd");
list.add("qwe");
for(String s : list){
sout(s); //结果应该是asd , qwe
}
ArrayList(实现类)
- 底层实现是数组,查询快,增删慢
案例
import java.util.ArrayList;
import java.util.Iterator;
// 需求:创建一个存储学生对象的集合,存储三个学生对象,实用程序实现在控制台遍历该集合
public class ListBianLi {
public static void main(String[] args) {
ArrayList<Student> stuArr= new ArrayList<Student>();
stuArr.add(new Student("gakki",30));
stuArr.add(new Student("樱木花道",20));
stuArr.add(new Student("巴菲特",68));
//第一正增强for
for(Student stu : stuArr){
System.out.println(stu);
}
System.out.println("--------");
//第二种迭代器
Iterator<Student> it= stuArr.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("--------");
//第三种普通for
for (int i = 0; i<stuArr.size() ; i++) {
System.out.println(stuArr.get(i));
}
}
}
LinkedList(实现类)
- 底层实现是链表,查询慢,增删快
Set(不可重复)(接口)
- 概述和特点
- set是一个不包含重复元素的集合
- 没有带索引的方法,不能用for循环遍历
- 哈希值
- 哈希值是JDK根据对象的地址或字符串或者数字算出来的int类型的数值
- Object类中有一个方法可以获取对象的哈希值
- public int hashcode();
- 同一个对象多次调用hashcode方法返回的哈希值相同
- 默认情况下不同对象的哈希值是不同的,而重写hashCode()方法,可以实现让不同对象的哈希值相同
HashSet(实现类)
- HashSet集合特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for遍历
- 由于是set集合所以是不包含重复元素的集合
- 案例
HashSet<String> hs = new HashSet<String>();
hs.add("hello");
hs.add("java");
hs.add("world");
hs.add("hello");
for(String s : hs){
sout(s);
}//结果为hello world java 证明不可重复无序
- HashSet集合保证元素唯一性原理
- HashSet为了保证唯一性需要在泛型中重写hashCode()和equals()
案例
public class Student {
private String name;
private int age;
public Student (String name1,int age1){
this.name = name1;
this.age = age1;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//为了保证向hashset中添加对象时不重复需要重写hashcode和equals方法
@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);
}
}
public class hashset01 {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<Student>();
hs.add(new Student("张曼玉",20));
hs.add(new Student("asd",21));
hs.add(new Student("张曼玉",20));
for(Student stu : hs){
System.out.println(stu);
}
}
}
输出
Student{name='张曼玉', age=20}
Student{name='asd', age=21}
LinkedHashSet(实现类)
- 哈希表和链表实现得Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复元素
- 案例
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("hello");
lhs.add("world");
lhs.add("java");
lhs.add("hello");
for(String s : lhs){
sout(s);
}
结果为hello,world,java
TreeSet(实现类)
- 间接继承Set接口
- 元素有序,这里的有序不是指存储和取出顺序一致,而是按照一定的规则排序,具体的排序方法取决于构造方法;
- TreeSet(); 根据其元素得自然排序进行排序
- TreeSet(Comparator comparator);根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以不包含重复元素
案例
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<>();
ts.add(50);
ts.add(20);
ts.add(10);
ts.add(111);
ts.add(888);
ts.add(20);
for(Integer num : ts){
System.out.println(num);
}
}
结果为10 20 50 111 888 有序且不重复
- 自然排序Comparable的使用
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student (String name1,int age1){
this.name = name1;
this.age = age1;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//为了让学生类能够排序需要实现Comparable接口
//返回值小于零认为当前对象比上一个小 大于零认为比上一个大 等于零认为相等所以不添加
@Override
public int compareTo(Student s) {
int num = this.age-s.age; //升序 反过来就是降序
int num2 = num==0?this.name.compareTo(s.name):num;//年龄差是否为0?如果为零就比较姓名,不为零就不用了
return num2;
}
}
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>();
ts.add(new Student("张曼玉",20));
ts.add(new Student("asd",21));
ts.add(new Student("张曼玉",20));
ts.add(new Student("asd",20));
for(Student stu : ts){
System.out.println(stu);
}
}
结果
Student{name='asd', age=20}
Student{name='张曼玉', age=20}
Student{name='asd', age=21}
- Comparartor比较器使用(TreeSet带参构造方法)
- 案例
public static void main(String[] args) {
//使用带参得TreeSet构造方法 创建一个匿名类重写compare方法
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge()-s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()):num;
return num2;
}
});
ts.add(new Student("张曼玉",20));
ts.add(new Student("asd",21));
ts.add(new Student("张曼玉",20));
ts.add(new Student("asd",20));
for(Student stu : ts){
System.out.println(stu);
}
}
结果同上 效果与学生类实现Comparable方法一样
双列集合(Map)(接口)
Map集合基本的功能
Map集合的获取功能
Map集合的遍历
方法一
public static void main(String[] args) {
Map<String , String> map = new HashMap<String , String>();
map.put("樱木花道","流川枫");
map.put("漩涡鸣人","宇智波佐助");
map.put("8848","背背佳");
Set<String> keyset = map.keySet(); //获取键的集合
for(String key: keyset){
String value = map.get(key);
System.out.println(key+","+value);
}
}
方法二
Map<String , String> map = new HashMap<String , String>();
map.put("樱木花道","流川枫");
map.put("漩涡鸣人","宇智波佐助");
map.put("8848","背背佳");
Set<Map.Entry<String, String>> entrySet = map.entrySet(); //获取所有键值对集合
for(Map.Entry<String, String> me: entrySet){
System.out.println(me.getKey()+","+me.getValue());
}
应用案例1
//需求创建一个HashMap集合,键是学生对象,值是居住地。存储多个键值对元素,并遍历。
//要求保证键的唯一性:如果学生对象成员变量的值都相同就认为是同一个对象
public static void main(String[] args) {
Map<Student , String> map = new HashMap<Student , String>();
Student stu1 = new Student(20,"流川枫");
Student stu2 = new Student(18,"樱木花道");
Student stu3 = new Student(21,"佐助");
Student stu4 = new Student(21,"佐助");
map.put(stu1,"北京");
map.put(stu2,"上海");
map.put(stu3,"武汉");
map.put(stu4,"沈阳");//学生作为键需要保证键的唯一性所有就要重写学生类得equals和hashcode方法
Set<Student> keySet = map.keySet();
for(Student key: keySet){
String value = map.get(key);
System.out.println(key.getAge()+","+key.getName()+","+value);
}
// 输出18,樱木花道,上海
// 20,流川枫,北京
// 21,佐助,沈阳
泛型
泛型方法与泛型类
public class Gic<T>{ //类
public void show(T t){
sout(t);
}
}
public class Gic{
public <T> void show(T t){
sout(t);
}
}
泛型接口
public interface Generic<T>{
void show(T t);
}
public class GenericImpl<T> implements Generic<T>{
void show(T t){
sout(t);
}
}
类型通配符
List<?> list1 = new ArrayList<Object>();
List<?> list1 = new ArrayList<Number>();
List<?> list1 = new ArrayList<Integer>();
//List<? extends Number> list1 = new ArrayList<Object>(); 不可以
List<? extends Number> list1 = new ArrayList<Number>();
List<? extends Number> list1 = new ArrayList<Integer>();
List<? super Number> list1 = new ArrayList<Object>();
List<? super Number> list1 = new ArrayList<Number>();
//List<? super Number> list1 = new ArrayList<Integer>(); 不可以
可变参数
public int sum(int... a){
int sum = 0;
for(int i : a){
sum+=i;
}
return sum;
}
public int sum(int a,int... b){ //如果要两个以上参数
return 0;
}
可变参数的使用
HashMap(实现类)
案例
//需求:键盘录入一个字符串,要求统计字符串中每个字符串出现的次数
//例如:键盘录入“asdasdasdfffea”在控制台输出"a4d3e1f3s3"
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入字符串");
String s = scanner.nextLine(); //获取键盘键入得字符串
HashMap<Character, Integer> charIntHs = new HashMap<>(); //创建hashmap
for (int i = 0; i < s.length() ; i++) { //循环遍历字符串 如果get到value就++如果没有get到就put
char key = s.charAt(i);
Integer value = charIntHs.get(key);
if(value==null){
charIntHs.put(key,1);
}else {
charIntHs.put(key,++value);
}
}
Set<Character> keySet = charIntHs.keySet(); //根据keyset遍历hashmap
for(Character ch : keySet){
Integer integer = charIntHs.get(ch);
System.out.println(ch+","+integer);
}
}
常见数据结构值哈希表
- JDK8之前,底层采用数组+链表实现 可以说是一个元素为了链表的数组
- 8之后在长度比较长的时候底层实现了优化
Collections应用(模拟斗地主,洗牌发牌)
Collections常用方法
案例
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
// 需求 通过程序实现斗地主过程中的洗牌,发牌和看牌 要求对牌进行排序
/*
* 思路:
* 1.创建HashMap,键是编号,值是牌
* 2.创建ArrayList,存储编号
* 3.创建花色数组和点数数组。
* 4.从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里存储编号
* 5.洗牌(洗的是编号)用Collections的shuffle()方法实现
* 6.发牌(发的是编号,为了保证编号是排序的,创建TreeSet集合接收)
* 7.定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
* 8.调用看牌的方法
*
* */
public class doudizhu {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
ArrayList<Integer> array = new ArrayList<>();
String[] point = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
String[] huase = {"♥", "♦", "♣", "♠"};
int index = 0;
for (int i = 0; i <point.length ; i++) {
for (int j = 0; j <huase.length ; j++) {
map.put(index,huase[j]+point[i]);
array.add(index);
index++;
}
}
map.put(index,"小王");
array.add(index);
index++;
map.put(index,"大王");
array.add(index);
Collections.shuffle(array);
TreeSet<Integer> lqxSet = new TreeSet<>();
TreeSet<Integer> jySet = new TreeSet<>();
TreeSet<Integer> sySet = new TreeSet<>();
TreeSet<Integer> dpSet = new TreeSet<>();
for (int i = 0; i <array.size() ; i++) {
int index1=array.get(i);
if(i>=array.size()-3){
dpSet.add(index1);
}else if(i%3==0){
lqxSet.add(index1);
}else if(i%3==1){
sySet.add(index1);
}else if(i%3==2){
jySet.add(index1);
}
}
lookPoker("林青霞",lqxSet,map);
lookPoker("结衣",jySet,map);
lookPoker("石原",sySet,map);
lookPoker("底牌",dpSet,map);
}
public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String > hm){
System.out.println(name+"的牌是:");
for (Integer key:ts) {
System.out.print(hm.get(key));
}
System.out.println();
}
}
结果:
林青霞的牌是:
♣3♥4♠4♥5♣6♥7♠7♠8♦9♠10♥J♠J♦Q♣Q♠Q♠A♥2
结衣的牌是:
♣4♣5♥6♦6♠6♣7♥8♦8♣8♥9♥10♣10♣J♣K♥A♦A♦2
石原的牌是:
♦3♠3♦5♠5♦7♣9♠9♦J♥Q♥K♦K♠K♣A♣2♠2小王大王
底牌的牌是:
♥3♦4♦10