集合和泛型
集合(方法和类)
集合:集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。java底层每一个集合对应一个数据结构,向不同的集合中存储数据就是将数据存放到了不同的数据结构当中。使用集合就是在使用不同的数据结构。
集合和数组的区别
数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象(实际上只是保存对象的引用变量,及对象的地址)。
集合的类型
java 集合类型分为 Collection和Map
Collection集合
Set和List。他们两个相反,Set实现不可重复,没有检索功能;List可重复可以检索。List接口主要实现的类由ArrayList和LinkedList,Set接口主要的实现类有HashSet和TreeSet两种。
Map集合
保存两个值其中一个值为key另一个为value ,key和value之间有一一映射的关系,通过key可以招到唯一的value值,其中的的key不可以重复,其中Mao实现类主要有HashMap和TreeMap两种。
Collection集合和Map集合它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。图 1 和图 2 分别为 Collection 和 Map 的子接口及其实现类。
Collection接口基本结构
Map接口基本结构
在 图 1 和图 2 中,黄色块为集合的接口,蓝色块为集合的实现类。
List接口常见方法
Arraylist内部数据存储结构是数组形式,在指定位置的元素时,会创建新的数组,因此不适合做大量的删除操作,但是这种数组结构允许通过索引的方式来访问元素。所以在查找和遍历元素时显得非常的高校。
ArrayList
ArrayList采用了数组这种数据结构
public class Main {
public static void main(String[] args) {
// write your code here
ArrayList list=new ArrayList();
list.add("num1");
list.add("num2");
list.add("num3");
System.out.println("集合长度:"+list.size());
System.out.println("第二个元素:"+list.get(1));
// System.out.println("删除第2个元素:"+list.remove(1));
System.out.println("数字首次出现的索引:"+list.indexOf("num1"));
System.out.println(list);
}
}
LinkedList
该类包含两个Node类型的结点frist和last属性来维护一个双向列表数,列表中的每一个元素都使用引用的方式来记住前一个元素和后一个元素,从而将所有元素都连接起来。
package com.company;
import java.util.ArrayList;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
// write your code here
LinkedList link=new LinkedList();
//添加元素
link.add("num1");
link.add("num2");
link.add("num3");
System.out.println(link);
link.offer("offer");//向集合尾部添加
link.push("push");//向集合头部添加
System.out.println(link);
//获取元素
Object obj=link.peek(); //获取集合第一个元素
System.out.println(obj);//输出集合种的元素
//删除元素
link.removeFirst(); //删除第一个元素
link.pollLast(); //删除集合最后一个元素
System.out.println(link);
}
遍历集合
遍历集合方式1:Iterator
- 所有集合都继承了Iterable接口,也就是所有的集合元素都是可以遍历。
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即返回一个迭代器
- Iterator只用于遍历集合,Iterator本身不存放对象
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
// write your code here
ArrayList list =new ArrayList();
list.add (1);
list.add(2);
list.add(3);
Iterator iterator= list.iterator();
while (iterator.hasNext())
{
Object obj=iterator.next();
System.out.println(obj);
}
}
}
运行图过程:
迭代器对象调用hasNext()方法判断是否有下一个元素,如果有迭代器调用next()方法向下移位。
遍历集合方式2:增强for循环
- 增强for循环底层就是迭代器
语法格式:
for(容器类元素类型 临时变量:容器变量){
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
// write your code here
ArrayList list =new ArrayList();
list.add (1);
list.add(2);
list.add(3);
for(Object obj:list){
System.out.println(obj);
}
}
}
注意:
1、使用foreach对集合只能够遍历,无法修改元素。
2、使用迭代器对集合中的对象进行迭代,如果调用集合对象remove()方法去删除元素,会出现异常。引起原因是修改了 集合的结构,无法获取下次执行的数据次数。
- 解决方案:
1.break
2.使用迭代器本身去删除
遍历集合方式3:forEach
使用forEach方法来遍历集合,该方法所需要的参数是一个函数式接口
public class Main {
public static void main(String[] args) {
ArrayList array =new ArrayList();
array.add("data");
array.add("data1");
array.add(1);
System.out.println(array);
array.forEach(o->{System.out.println("迭代集合元素为:"+o);});
//forEach方法在执行式,会自动遍历集合元素并将元素逐个传递给Lambda表达式的形参
}
}
Set接口
Set接口无序(存进去的数据和取出来的数据不一致,不是会进行排序),不可以存放重复元素。主要有HashSet和TreeSet这两个实现类
HashSet
import java.util.*;
public class Main {
public static void main(String[] args) {
HashSet hash =new HashSet();
hash.add("data");
hash.add("data1");
hash.add(1);
hash.add(1);
System.out.println(hash);
hash.forEach(o->{System.out.println("迭代集合元素为:"+o);});
}
}
在调用HashSet集合add方法时首先会调用hashCode()方法对象的hash值—》找到对象的存储位置—》判断该位置对象是否存在----》存在调用equal方法(判断是否想等,相等丢弃)—》不存在—》调用add()方法向HashSet集合添加元素。
注意
要求存在对象时需要重写Object中的hashCode()和equals()方法。因为String类里面已经重写了HashCode()和equals()方法。这里与Collection类里面的contain函数一样,equals比较的是内容。
TreeSet
底层通过平衡二叉树来实现
package com.company;
import com.sun.deploy.panel.ITreeNode;
import java.sql.ClientInfoStatus;
import java.util.*;
public class Main<T> {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>();
set.add("a");
set.add("d");
set.add("z");
set.add("c");
set.add("A");
Iterator<String> iter=set.iterator();
while (iter.hasNext()){
String a=iter.next();
System.out.println(a);
}
}
}
在向TreeSet集合中添加元素,无论元素的顺序如何,这些元素都可以按照一定的顺序排列。其原因是将插入的数据和其他元素进行比较,比较会调用compareTo()方法,该方法时在Comparable接口中定义的,因此想要默认对集合中的元素进行排序就需要实现Comparable接口。java中大部分数据都实现了CompareTo()方法,如Intger,Double等,如果用户需要存放一些自定义的数据就需要使用TreeSet排序规则,分别为自然排序和定制排序。
Map
map集合常见的有两种:HashMap和TreeMap。HashMap底层是哈希表,TreeMap底层是二叉树。
遍历HashMap:1
package com.company;
import sun.util.resources.ms.CalendarData_ms_MY;
import java.lang.invoke.MutableCallSite;
import java.util.*;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
Map<Integer, String> users = new HashMap();
users.put(11, "张11"); // 将学生信息键值对存储到Map中
users.put(22,"赵22");
users.put(33,"钱33");
users.put(44,"宋44");
Set<Integer> keys = users.keySet();//以set集合的形式返回map集合中所有的键值对value
System.out.println(keys);
Iterator<Integer> it=keys.iterator();
while (it.hasNext()){
//取出一个key
Integer key= it.next();
//通过key来获取value
String value =users.get(key);
System.out.println(key+"="+value);
}
}
}
Map.Put(k,v)实现原理
从以上可以得到HashMap集合的key,会先调用两个方法,一个是hashCode(),一个方法是equals(),那这个两个方法都要重写。在向HashSet中放值时,就将值放到HashMap的key上面了,因此得到HashMap的key就是和HashSet获取值的方式一致。因此HashSet集合中的元素也需要同时重写hashCode()+equals()方法。
遍历HashMap方式:2
将Map集合全部转换成Set集合,其中Set集合的类型为Map.Entry(Map.Entry是静态内部类)
import java.util.*;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
Set<Map.Entry<Integer,String >> set= map.entrySet();
Iterator<Map.Entry<Integer,String >> key=set.iterator();
while (key.hasNext())
{
Map.Entry<Integer,String > node=key.next();
Integer key1=node.getKey();
String value1=node.getValue();
System.out.println(key1+"="+value1);
}
for(Map.Entry<Integer,String> obj:set) {
System.out.println(obj.getKey()+"--->"+obj.getValue());
}
}
}
注意:HashMap初始化容量必须时2的倍数,默认初始化为16.
HashMap数据结构
哈希表是数据表和单向链表的结合体
数组:在查询方面效率高,随机删除方面效率低
单向链表:在随机删减方面效率较高,在查询方面效率低
HashMap集合底层代码
public class HashMap{
//HashMap底层是一维数组
Node <k,v>[] table;
//静态内部类Hash.Node
static class Node<k,v>{
final int hash;// 哈希值(哈希值是key的hashCode()方法执行结果,hash值通过hah算法得到)
final k key;//存储到Map集合中的那个key
v value;//存储到Map集合中的那个value
Node<k,v> next;//下个节点的内存地址
}
}
哈希表:一维数组,这个数组中的每一个元素是单向链表
泛型(可以把他看成一种数据类型)
写在< >
里面的数据类型,他是用来约束集合可以储存的数据类型。< >
只能是引用类型,不能是简单数据类型。
泛型参数可以有多个。
常见的泛型种类
常见的有泛型类、泛型方法、泛型接口
泛型类:
class A<T>{ }
这里T可以为任意值,在使用的时候可以对其指定。
泛型接口:
interface Generator<T>{T next();}
泛型方法:
class c{
public void test(string s){
return s;
}
}
把普通方法改写成泛型方法
public <T> void test( T s){
return s;
}
public string test1(string s){
return s;
}
把普通方法改写成泛型方法
public <T> T test1 (T s){
return s;
}
通配符:?
泛型类
package com.company;
import java.util.*;
public class Main {
public static void main(String[] args) {
/* List MyList = new ArrayList();
Dog daHuang = new Dog();
Cat xiaoHuan = new Cat();
MyList.add(daHuang);
MyList.add(xiaoHuan);
Iterator iter = MyList.iterator();
while (iter.hasNext()) {
// 没有这个语法,通过迭代器取出来就是Object
// Animal obj=iter.next();
//obj中没有move方法,无法调用
Object obj = iter.next();
if(obj instanceof Animal) {
Animal animal=(Animal) obj;
animal.move();
}
}
*/
List<Animal> MyList = new ArrayList<>();
Dog daHuang = new Dog();
Cat xiaoHuan = new Cat();
MyList.add(daHuang);
MyList.add(xiaoHuan);
Iterator<Animal> iter = MyList.iterator();
while (iter.hasNext()){
//迭代器每一次返回都是Animal类型
Animal a=iter.next();
a.move();
if(a instanceof Dog){
((Dog) a).blak();
}
}
}
}
class Animal{
public void move(){
System.out.println("动物在移动");
}
}
class Dog extends Animal{
public void blak(){
System.out.println("dog blak 人l");
}
}
class Cat extends Animal {
public void cat() {
System.out.println("cat black 人了");
}
}
使用泛型之后,在相对应的集合当中只可以保存相对应的数量类型,也就可以调用相对应的数据类型的函数。
自定泛型
集合练习—随机发牌
package com.company;
import java.util.*;
public class Main {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<>();
String[] colors = {"♣", "♦", "♥", "♠"};
String[] number = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
//向集合中添加牌
for (String color : colors) {
for (String num : number) {
array.add(color + num);
}
}
array.add("大王");
array.add("小王");
System.out.println(array);
/*Iterator<String> iter = array.iterator();
while(iter.hasNext()) {
String it = iter.next();
System.out.println(it);
}
*/
//将牌随机打乱
Collections.shuffle(array);
//创建不同集合
ArrayList<String> daHuang=new ArrayList<>();
ArrayList<String> daBai=new ArrayList<>();
ArrayList<String> Bai=new ArrayList<>();
ArrayList<String> dipai=new ArrayList<>();
//向不同的集合中添加牌
for (int i=0; i< array.size();i++){
String puke= array.get(i);
if(i>=array.size()-3){
dipai.add(puke);
}else if (i%3==0){
daHuang.add(puke);
}else if (i%3==1){
daBai.add(puke);
}else if(i%3==2){
Bai.add(puke);
}
}
lookPuke("daBai",daBai);
lookPuke("daHuang",daHuang);
lookPuke("Bai",Bai);
lookPuke("depai",dipai);
}
//对集合中的牌输出
public static void lookPuke(String name,ArrayList<String> array){
System.out.print(name+"看牌是");
for(String arr:array ){
System.out.print(arr+" ");
}
System.out.println( " ");
}
}
集合练习升级—随机发牌
package Ceshi;
import com.company.Main;
import java.util.*;
public class Except {
public static void main(String [] args){
Map<Integer ,String> hm=new HashMap<>();
ArrayList<Integer> array=new ArrayList<>();
String[] num=new String[]{"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
String[] colors={"♣", "♦", "♥", "♠"};
int index=0;
for(String nums:num){
for(String color:colors){
hm.put(index,color+nums);
array.add(index);
index++;
}
}
/* System.out.println(index);*/
hm.put(index,"大王");
array.add(index);
index++;
hm.put(index,"小王");
array.add(index);
Collections.shuffle(array);
TreeSet<Integer> lxSet=new TreeSet<>();
TreeSet<Integer> zzqSet=new TreeSet<>();
TreeSet<Integer> ydfSet=new TreeSet<>();
TreeSet<Integer> dpSet=new TreeSet<>();
//System.out.println(array.size());
//向不同的集合中添加牌
for (int i=0; i< array.size();i++){
Integer puke= array.get(i);
if(i>=array.size()-3){
dpSet.add(puke);
}else if (i%3==0){
zzqSet.add(puke);
}else if (i%3==1){
ydfSet.add(puke);
}else {
lxSet.add(puke);
}
}
lookPoker("鲁迅",lxSet,hm);
lookPoker("朱自清",lxSet,hm);
lookPoker("郁达夫",lxSet,hm);
lookPoker("底牌",lxSet,hm);
}
//在TreeSet中找编号然后取TreeMap中找对应的牌
public static void lookPoker(String name,TreeSet<Integer> ts,Map<Integer,String> hp){
System.out.println(name+"的牌");
for (Integer key:ts){
String poker=hp.get(key);
System.out.print(poker+" ");
}
System.out.println( );
}
}