java-集合框架

集合框架

先看个思维导图呗
在这里插入图片描述

一.结构图

在这里插入图片描述
在这里插入图片描述

二.ArrayList

1.与数组的区别

使用数组的局限性

如果要存放多个对象,可以使用数组,但是数组有局限性。

package charactor;
 
public class Hero {
    public String name;
    public float hp;
    public int damage;
    public Hero() {
    }
    // 增加一个初始化name的构造方法
    public Hero(String name) {
        this.name = name;
    }
    // 重写toString方法
    public String toString() {
        return name;
    }
}
package collection;
import charactor.Hero;
 
public class TestCollection {
    public static void main(String[] args) {
        //数组的局限性
        Hero heros[] = new Hero[10];
        //声明长度是10的数组
        //不用的数组就浪费了
        //超过10的个数,又放不下
        heros[0] = new Hero("盖伦");
        //放不下要报错
        heros[20] = new Hero("提莫");      
    }    
}

三.ArrayList存放对象

为了解决数组的局限性,引入容器类的概念。
最常见的容器类就是ArrayList。
容器的容量"capacity"会随着对象的增加,自动增长。
只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
	//告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。
    @SuppressWarnings("rawtypes")
    public static void main(String[] args) {
        //容器类ArrayList,用于存放对象
        ArrayList heros = new ArrayList();
        heros.add( new Hero("盖伦"));
        System.out.println(heros.size());
         
        //容器的容量"capacity"会随着对象的增加,自动增长
        //只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
        heros.add( new Hero("提莫"));
        System.out.println(heros.size());    
    }    
}

2.常用方法

add 增加

add 有两种用法
第一种是直接add对象,把对象加在最后面
heros.add(new Hero("hero " + i));

第二种是在指定位置加对象
heros.add(3, specialHero);

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 把5个对象加入到ArrayList中
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        System.out.println(heros);
 
        // 在指定位置增加对象
        Hero specialHero = new Hero("special hero");
        heros.add(3, specialHero);
        System.out.println(heros);
        System.out.println(heros.toString());
    } 
}

在这里插入图片描述
contains 判断是否存在

通过方法contains 判断一个对象是否在容器中
判断标准: 是否是同一个对象,而不是name是否相同

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
 
        System.out.println(heros);
        // 判断一个对象是否在容器中
        // 判断标准: 是否是同一个对象,而不是name是否相同
        System.out.print("虽然一个新的对象名字也叫 hero 1,但是contains的返回是:");
        System.out.println(heros.contains(new Hero("hero 1")));
        System.out.print("而对specialHero的判断,contains的返回是:");
        System.out.println(heros.contains(specialHero));
    }
}

在这里插入图片描述
get 获取指定位置的对象

通过get获取指定位置的对象,如果输入的下标越界,一样会报错

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
	public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }      
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
        System.out.println(heros);
        //获取指定位置的对象
        System.out.println("下标是5的对象是:"+heros.get(5));
        //如果超出了范围,依然会报错
        System.out.println(heros.get(6));
 
    }
}

在这里插入图片描述
indexOf 获取对象所处的位置

indexOf用于判断一个对象在ArrayList中所处的位置
与contains一样,判断标准是对象是否相同,而非对象的name值是否相等

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
	public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
		// 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
 
        System.out.println(heros);
        System.out.println("specialHero所处的位置:"+heros.indexOf(specialHero));
        System.out.println("新的英雄,但是名字是\"hero 1\"所处的位置:"+heros.indexOf(new Hero("hero 1")));
 
    }
}

在这里插入图片描述
remove 删除

remove用于把对象从ArrayList中删除
remove可以根据下标删除ArrayList的元素
heros.remove(2);
也可以根据对象删除
heros.remove(specialHero);

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
         
        System.out.println(heros);
        heros.remove(2);
        System.out.println("删除下标是2的对象");
        System.out.println(heros);
        System.out.println("删除special hero");
        heros.remove(specialHero);
        System.out.println(heros); 
    }
}

在这里插入图片描述
set 替换

set用于替换指定位置的元素

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
         
        System.out.println(heros);
        System.out.println("把下标是5的元素,替换为\"hero 5\"");
        heros.set(5, new Hero("hero 5"));
        System.out.println(heros);
    }
}

在这里插入图片描述
size 获取大小

size 用于获取ArrayList的大小

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
        System.out.println(heros);
        System.out.println("获取ArrayList的大小:");
        System.out.println(heros.size());
    }
}

在这里插入图片描述
toArray 转换为数组

toArray可以把一个ArrayList对象转换为数组。
需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
        Hero specialHero = new Hero("special hero");
        heros.add(specialHero);
        System.out.println(heros);
        Hero hs[] = (Hero[])heros.toArray(new Hero[]{});
        System.out.println("数组:" +hs);
        for(Hero HS:hs){
        	  System.out.print(HS+"  ");
        }
    }
}

在这里插入图片描述
addAll 把另一个容器所有对象都加进来

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
 
        System.out.println("ArrayList heros:\t" + heros);
          
        //把另一个容器里所有的元素,都加入到该容器里来
        ArrayList anotherHeros = new ArrayList();
        anotherHeros.add(new Hero("hero a"));
        anotherHeros.add(new Hero("hero b"));
        anotherHeros.add(new Hero("hero c"));
        System.out.println("anotherHeros heros:\t" + anotherHeros);
        heros.addAll(anotherHeros);
        System.out.println("把另一个ArrayList的元素都加入到当前ArrayList:");
        System.out.println("ArrayList heros:\t" + heros);
         
    }
}

在这里插入图片描述
clear 清空

clear 清空一个ArrayList

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        }
 
        System.out.println("ArrayList heros:\t" + heros);
        System.out.println("使用clear清空");
        heros.clear();
        System.out.println("ArrayList heros:\t" + heros);    
    }
}

在这里插入图片描述
练习

判断是否相同

如果就是要判断集合里是否存在一个 name等于 "hero 1"的对象,应该怎么做?

package charactor;
public class Hero {
    public String name;
    public float hp;
    public int damage;
    public Hero() {
    	System.out.println("Hero的构造方法 ");
    }
    // 增加一个初始化name的构造方法
    public Hero(String name) {
        this.name = name;
    
	// 重写toString方法
    public String toString() {
        return name;
    }
}
package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
	public static void main(String[] args) {
        ArrayList<Hero> heros=new ArrayList();
        
        heros.add(new Hero("Hero 1"));
        heros.add(new Hero("Hero 2"));
        // 初始化5个对象
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero " + i));
        } 
        System.out.println(heros);
        for(int i=0;i<heros.size();i++) {
        	Hero h=heros.get(i);    
        	//System.out.println(h);
            if(h.toString().equals("Hero 1")){
                System.out.println("存在一个名字为Hero 1的对象");
                break;
            }
        }   
    }
}

在这里插入图片描述
MyStringBuffer

做一个一样的MyStringBuffer练习,但是不使用字符数组,而是使用ArrayList来实现。

package collection;
public interface IStringBuffer {
    public void append(String str); //追加字符串
    public void append(char c);  //追加字符
    public void insert(int pos,char b); //指定位置插入字符
    public void insert(int pos,String b); //指定位置插入字符串
    public void delete(int start); //从开始位置删除剩下的
    public void delete(int start,int end); //从开始位置删除结束位置-1
    public void reverse(); //反转
    public int length(); //返回长度
}
package collection;
import java.util.ArrayList;
public class MyStringBuffer implements IStringBuffer {
    ArrayList sb=new ArrayList();
    
    public MyStringBuffer() {
    }
 
    public MyStringBuffer(String string) {
         if(null==string)
             return;
    	char cs[] = string.toCharArray();
        
        for (int i = 0; i < string.length(); i++) {;
            sb.add(cs[i]);
        }
    }
 
    @Override
    public void append(String str) {
        this.insert(sb.size(), str);
    }
 
    @Override
    public void append(char c) {
    	append(String.valueOf(c));
    }
 
    @Override
    public void insert(int pos, char b) {
    	insert(pos,String.valueOf(b));
    }
 
    @Override
    public void insert(int pos, String b) {
    	// 边界条件判断
        if (pos < 0)
            return;
  
        if (pos > sb.size())
            return;
  
        if (null == b)
            return;
  
        // 无需手动扩容
        char[] cs = b.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            sb.add(pos + i, cs[i]);
        }
    }
 
    @Override
    public void delete(int start) {
        this.delete(start, sb.size());
    }
 
    @Override
    public void delete(int start, int end) {
    	//边界条件判断
        if(start<0)
            return;
          
        if(start>sb.size())
            return;
          
        if(end<0)
            return;
          
        if(end>sb.size())
            return;
          
        if(start>=end)
            return;
        
        for (int i = 0; i < end - start; i++) {
            sb.remove(start);
        }
    }
 
    @Override
    public void reverse() {
        for (int i = 0; i < sb.size()/2 ; i++) {
            char c = (char) sb.get(i);
            sb.set(i, sb.get(sb.size() - i-1));
            sb.set(sb.size() - i-1, c);
        }
    }
 
    @Override
    public int length() {
        return sb.size();
    }
 
    @Override
    public String toString() {
    	char[] charValue = new char[sb.size()];
        for (int i = 0; i < sb.size(); i++) {
            charValue[i] = (char) sb.get(i);
        }
        return new String(charValue);
    }
 
    public static void main(String[] args) {
        MyStringBuffer sb = new MyStringBuffer("there light");
        System.out.println(sb);
        sb.insert(0, "let ");
        System.out.println(sb);
 
        sb.insert(10, "be ");
        System.out.println(sb);
        sb.insert(0, "God Say:");
        System.out.println(sb);
        sb.append("!");
        System.out.println(sb);
        sb.append('?');
        System.out.println(sb);
 
        sb.reverse();
        System.out.println(sb);
 
        sb.reverse();
        System.out.println(sb);
 
        sb.delete(0, 4);
        System.out.println(sb);
        sb.delete(4);
        System.out.println(sb);
 
    }
}

在这里插入图片描述
3.List接口

ArrayList和List

ArrayList实现了接口List
常见的写法会把引用声明为接口List类型
注意:是java.util.List,而不是java.awt.List
在这里插入图片描述

package collection;
import java.util.ArrayList;
import java.util.List;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        //ArrayList实现了接口List
        //常见的写法会把引用声明为接口List类型
        //注意:是java.util.List,而不是java.awt.List
        //接口引用指向子类对象(多态)
         
        List heros = new ArrayList();
        heros.add( new Hero("盖伦"));
        System.out.println(heros.size());     
    } 
}

List接口的方法

因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。
ArrayList 常用方法有详细的讲解。

4.泛型Generic

不指定泛型的容器,可以存放任何类型的元素
指定了泛型的容器,只能存放指定类型的元素以及其子类

package property;
public class Item {
    String name;
    int price;  
    public Item(){       
    }
    //提供一个初始化name的构造方法
    public Item(String name){
        this.name = name;
    } 
    public void effect(){
        System.out.println("物品使用后,可以有效果");
    }  
}
package collection;
import java.util.ArrayList;
import java.util.List;
import property.Item;
import charactor.APHero;
import charactor.Hero;
public class TestCollection { 
    public static void main(String[] args) {   
        //对于不使用泛型的容器,可以往里面放英雄,也可以往里面放物品
        List heros = new ArrayList();
        heros.add(new Hero("盖伦"));   
        //本来用于存放英雄的容器,现在也可以存放物品了
        heros.add(new Item("冰杖"));
        //对象转型会出现问题
        Hero h1=  (Hero) heros.get(0);
        //尤其是在容器里放的对象太多的时候,就记不清楚哪个位置放的是哪种类型的对象了
        Hero h2=  (Hero) heros.get(1);    
        //引入泛型Generic
        //声明容器的时候,就指定了这种容器,只能放Hero,放其他的就会出错
        List<Hero> genericheros = new ArrayList<Hero>();
        genericheros.add(new Hero("盖伦"));
        
        //如果不是Hero类型,根本就放不进去
        //genericheros.add(new Item("冰杖"));
          
        //除此之外,还能存放Hero的子类
        genericheros.add(new APHero());
         
        //并且在取出数据的时候,不需要再进行转型了,因为里面肯定是放的Hero或者其子类
        Hero h = genericheros.get(0);     
    }    
}

泛型的简写

为了不使编译器出现警告,需要前后都使用泛型,像这样:
List genericheros = new ArrayList();

不过JDK7提供了一个可以略微减少代码量的泛型简写方式
List genericheros2 = new ArrayList<>();
后面的泛型可以用<>来代替,聊胜于无吧

package collection;
import java.util.ArrayList;
import java.util.List;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> genericheros = new ArrayList<Hero>();
        List<Hero> genericheros2 = new ArrayList<>();
    }     
}

泛型的系统学习
泛型的知识还包含 支持泛型的类 泛型转型 通配符 这些内容都在泛型章节详细展开。

5.遍历

for 用for循环遍历

用size()和get()分别得到大小,和获取指定位置的元素,结合for循环就可以遍历出ArrayList的内容。

package collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> heros = new ArrayList<Hero>();
        // 放5个Hero进入容器
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero name " + i));
        }
        // 第一种遍历 for循环
        System.out.println("--------for 循环-------");
        for (int i = 0; i < heros.size(); i++) {
            Hero h = heros.get(i);
            System.out.println(h);
        }
    }
}

在这里插入图片描述
iterator 迭代器遍历

用迭代器Iterator遍历集合中的元素
在这里插入图片描述

package collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> heros = new ArrayList<Hero>();
         
        //放5个Hero进入容器
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero name " +i));
        }
         
        //第二种遍历,使用迭代器
        System.out.println("--------使用while的iterator-------");
        Iterator<Hero> it= heros.iterator();
        //从最开始的位置判断"下一个"位置是否有数据
        //如果有就通过next取出来,并且把指针向下移动
        //直到"下一个"位置没有数据
        while(it.hasNext()){
            Hero h = it.next();
            System.out.println(h);
        }
        //迭代器的for写法
        System.out.println("--------使用for的iterator-------");
        for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
            Hero hero = (Hero) iterator.next();
            System.out.println(hero);
        }  
    }  
}

for: 用增强型for循环

使用增强型for循环可以非常方便的遍历ArrayList中的元素,这是很多开发人员的首选。
不过增强型for循环也有不足:
无法用来进行ArrayList的初始化
无法得知当前是第几个元素了,当需要只打印单数元素的时候,就做不到了。 必须再自定下标变量。

package collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> heros = new ArrayList<Hero>();
 
        // 放5个Hero进入容器
        for (int i = 0; i < 5; i++) {
            heros.add(new Hero("hero name " + i));
        }
 
        // 第三种,增强型for循环
        System.out.println("--------增强型for循环-------");
        for (Hero h : heros) {
            System.out.println(h);
        }
    }
}

在这里插入图片描述
练习-删除ArrayList中的数据

首先初始化一个Hero集合,里面放100个Hero对象,名称分别是从
hero 0
hero 1
hero 2

hero 99.

通过遍历的手段,删除掉名字编号是8的倍数的对象
方法一
思路:根据对象内容的后缀满足8倍数,此时的i删除即可,就不用考虑删除造成的下标变化。

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
	public static void main(String[] args) {
        ArrayList heros = new ArrayList();
 
        for (int i = 0; i < 100; i++) {
        	heros.add(new Hero("hero" + " " + i));
        }
        for (int i = 0; i < heros.size(); i++) {
        	if (Integer.parseInt((heros.get(i).toString().substring(5)))%8==0&&Integer.parseInt((heros.get(i).toString().substring(5)))!=0) {
                heros.remove(i);
            }
        }
        System.out.println(heros);
    }	
}

方法二

package collection;
import java.util.ArrayList;
import charactor.Hero;
public class TestCollection {
	public static void main(String[] args) {
        ArrayList heros = new ArrayList();
        for (int i = 0; i < 100; i++) {
        	heros.add(new Hero("hero" + " " + i));
        }
        System.out.println(heros);
        int j=(int)heros.size()/8;
        for (int i = 1; i <=j; i++) {
         heros.remove(8*i-(i-1));
        }
        System.out.println(heros);
    }	
}

四.其他集合

1.LinkedList

定义

序列分先进先出FIFO,先进后出FILO
FIFO在Java中又叫Queue 队列
FILO在Java中又叫Stack 栈

LinkedList 与 List接口
在这里插入图片描述
与ArrayList一样,LinkedList也实现了List接口,诸如add,remove,contains等等方法。 详细使用,请参考 ArrayList 常用方法。
接下来要讲的是LinkedList的一些特别的地方。

双向链表 - Deque

除了实现了List接口外,LinkedList还实现了双向链表结构Deque,可以很方便的在头尾插入删除数据。

什么是链表结构: 与数组结构相比较,数组结构,就好像是电影院,每个位置都有标示,每个位置之间的间隔都是一样的。 而链表就相当于佛珠,每个珠子,只连接前一个和后一个,不用关心除此之外的其他佛珠在哪里。
在这里插入图片描述

package collection;
import java.util.LinkedList;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        //LinkedList是一个双向链表结构的list
        LinkedList<Hero> ll =new LinkedList<Hero>();
         
        //所以可以很方便的在头部和尾部插入数据
        //在最后插入新的英雄
        ll.addLast(new Hero("hero1"));
        ll.addLast(new Hero("hero2"));
        ll.addLast(new Hero("hero3"));
        System.out.println(ll);
         
        //在最前面插入新的英雄
        ll.addFirst(new Hero("heroX"));
        System.out.println(ll);
         
        //查看最前面的英雄
        System.out.println(ll.getFirst());
        //查看最后面的英雄
        System.out.println(ll.getLast());
         
        //查看不会导致英雄被删除
        System.out.println(ll);
        //取出最前面的英雄
        System.out.println(ll.removeFirst());
         
        //取出最后面的英雄
        System.out.println(ll.removeLast());
         
        //取出会导致英雄被删除
        System.out.println(ll);    
    }  
}

队列 - Queue

LinkedList 除了实现了List和Deque外,还实现了Queue接口(队列)。
Queue是先进先出队列 FIFO,常用方法:
offer 在最后添加元素
poll 取出第一个元素
peek 查看第一个元素

package collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        //和ArrayList一样,LinkedList也实现了List接口
        List ll =new LinkedList<Hero>();
          
        //所不同的是LinkedList还实现了Deque,进而又实现了Queue这个接口
        //Queue代表FIFO 先进先出的队列
        Queue<Hero> q= new LinkedList<Hero>();
          
        //加在队列的最后面
        System.out.print("初始化队列:\t");
        q.offer(new Hero("Hero1"));
        q.offer(new Hero("Hero2"));
        q.offer(new Hero("Hero3"));
        q.offer(new Hero("Hero4"));
          
        System.out.println(q);
        System.out.print("把第一个元素取poll()出来:\t");
        //取出第一个Hero,FIFO 先进先出
        Hero h = q.poll();
        System.out.println(h);
        System.out.print("取出第一个元素之后的队列:\t");
        System.out.println(q);
          
        //把第一个拿出来看一看,但是不取出来
        h=q.peek();
        System.out.print("查看peek()第一个元素:\t");
        System.out.println(h);
        System.out.print("查看并不会导致第一个元素被取出来:\t");
        System.out.println(q);    
    }    
}

练习-使用LinkedList实现Stack栈

与FIFO(先入先出的)队列类似的一种数据结构是FILO(先入后出)栈Stack
根据接口Stack :
实现类:MyStack

public class MyStack implements Stack

并向这个栈中,压入5个英雄,接着弹出5个英雄

package collection;
import charactor.Hero;
public interface Stack {
    //把英雄推入到最后位置
    public void push(Hero h);
    //把最后一个英雄取出来
    public Hero pull();
    //查看最后一个英雄
    public Hero peek();
}
package collection;
import java.util.LinkedList;
import java.util.List;
import property.Hero;
/*
 *  使用LinkedList实现Stack栈
 * */
public class MyStack implements Stack{
     
    //创建一个LinkedList对象就可以了
    LinkedList<Hero> ll=new LinkedList<Hero>();
 
    @Override
    //把英雄推入到最后位置
    public void push(Hero h) {
        //每次将传入的对象都添加到链表的w尾部
        ll.addLast(h);  
    }
 
    @Override
    //把最后一个英雄取出来
    public Hero pull() {
        //删除链表的尾部并返回
        return ll.removeLast();
    }
 
    @Override
    //查看最后一个英雄
    public Hero peek() {
        //查看链表的尾部并返回
        return ll.getLast();
    }
     
    @Override
    public String toString() {
        return ll.toString();
    }
 
    public static void main(String[] args) {
        //创建MyStack对象
        MyStack ms=new MyStack();
         
        //往MyStack对象中添加英雄
        ms.push(new Hero("hero1"));
        ms.push(new Hero("hero2"));
        ms.push(new Hero("hero3"));
        ms.push(new Hero("hero4"));
        ms.push(new Hero("hero5"));
        System.out.println("MyStack中的英雄顺序为:"+ms);
         
        System.out.println("MyStack中最后一个英雄是:"+ms.peek());
         
        //取出最后一个英雄
        Hero h=ms.pull();
        System.out.println("把MyStack取出之后的英雄顺序为:"+ms);   
    }
}

在这里插入图片描述
2.二叉树

概念

二叉树由各种节点组成。
二叉树特点:
每个节点都可以有左子节点,右子节点。
每一个节点都有一个值。
在这里插入图片描述

package collection;
public class Node {
    // 左子节点
    public Node leftNode;
    // 右子节点
    public Node rightNode;
    // 值
    public Object value;
}

插入数据

假设通过二叉树对如下10个随机数进行排序
67,7,30,73,10,0,78,81,10,74
排序的第一个步骤是把数据插入到该二叉树中
插入基本逻辑是,小、相同的放左边,大的放右边

  1. 67 放在根节点
  2. 7 比 67小,放在67的左节点
  3. 30 比67 小,找到67的左节点7,30比7大,就放在7的右节点
  4. 73 比67大, 放在67的右节点
  5. 10 比 67小,找到67的左节点7,10比7大,找到7的右节点30,10比30小,放在30的左节点。

  6. 10比67小,找到67的左节点7,10比7大,找到7的右节点30,10比30小,找到30的左节点10,10和10一样大,放在左边
    在这里插入图片描述
package collection;
public class Node {
    // 左子节点
    public Node leftNode;
    // 右子节点
    public Node rightNode;
    // 值
    public Object value;
  
    // 插入 数据
    public void add(Object v) {
        // 如果当前节点没有值,就把数据放在当前节点上
        if (null == value)
            value = v;
  
        // 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
        else {
            // 新增的值,比当前值小或者相同   
            if ((Integer) v -((Integer)value) <= 0) {
                if (null == leftNode)
                    leftNode = new Node();
                leftNode.add(v);
            }
            // 新增的值,比当前值大
            else {
                if (null == rightNode)
                    rightNode = new Node();
                rightNode.add(v);
            }
        }
    }
  
    public static void main(String[] args) {
        int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
  
        Node roots = new Node();
        for (int number : randoms) {
            roots.add(number);
        }
    }
}

排序-遍历

通过上一个步骤的插入行为,实际上,数据就已经排好序了。 接下来要做的是看,把这些已经排好序的数据,遍历成我们常用的List或者数组的形式

二叉树的遍历分左序,中序,右序
左序即: 中间的数遍历后放在左边
中序即: 中间的数遍历后放在中间
右序即: 中间的数遍历后放在右边
如图所见,我们希望遍历后的结果是从小到大的,所以应该采用中序遍历
在这里插入图片描述

package collection;
import java.util.ArrayList;
import java.util.List;
public class Node {
    // 左子节点
    public Node leftNode;
    // 右子节点
    public Node rightNode;
    // 值
    public Object value;
  
    // 插入 数据
    public void add(Object v) {
        // 如果当前节点没有值,就把数据放在当前节点上
        if (null == value)
            value = v;
  
        // 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
        else {
            // 新增的值,比当前值小或者相同
             
            if ((Integer) v -((Integer)value) <= 0) {
                if (null == leftNode)
                    leftNode = new Node();
                leftNode.add(v);
            }
            // 新增的值,比当前值大
            else {
                if (null == rightNode)
                    rightNode = new Node();
                rightNode.add(v);
            }
  
        }
  
    }
  
 // 中序遍历所有的节点
    public List<Object> values() {
        List<Object> values = new ArrayList<>();
  
        // 左节点的遍历结果
        if (null != leftNode){
            values.addAll(leftNode.values());
        }
         
        // 当前节点
        values.add(value);
  
        // 右节点的遍历结果
        if (null != rightNode){
            values.addAll(rightNode.values());
        }
         
        return values;
    }
  
    public static void main(String[] args) {
  
        int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
  
        Node roots = new Node();
        for (int number : randoms) {
            roots.add(number);
        }
  
        System.out.println(roots.values());
    }
}

在这里插入图片描述
练习

英雄二叉树

根据上面的学习和理解,设计一个Hero二叉树,HeroNode。
可以向这个英雄二叉树插入不同的Hero对象,并且按照Hero的血量倒排序。

随机生成10个Hero对象,每个Hero对象都有不同的血量值,插入这个HeroNode后,把排序结果打印出来。

package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HeroNode {
    public HeroNode leftNode;
    public HeroNode rightNode;
    public HeroNode value;
    String hero_name;
    float hp;
    public HeroNode(String hero_name,float hp){
        this.hero_name=hero_name;
        this.hp=hp;
    }
    public String toString() {
        return "[name:" + hero_name + ", hp=" + hp + "]";
    }
    public HeroNode() {
 
    }
    public void add(HeroNode v){
        if(value==null){
            value=v;
        }else{
            if((v.hp-value.hp)<=0){
                if(leftNode==null) {
                    leftNode = new HeroNode();
                }
                    leftNode.add(v);
            }else{
                if(rightNode==null) {
                    rightNode = new HeroNode();
                }
                    rightNode.add(v);
            }
        }
    }
    public List<Object> values(){
        List<Object> values = new ArrayList<>();
        if(null!=rightNode){
            values.addAll(rightNode.values());
        }
        values.add(value);
        if(null!=leftNode){
            values.addAll(leftNode.values());
        }
        return values;
    }
    public static void main(String[] args){
        List<HeroNode> hn=new ArrayList<>();
        System.out.println("初始化10个hero:");
        for(int i=0;i<10;i++){
            hn.add(new HeroNode("hero"+i,(int) (Math.random()*1000)));
        }
        System.out.println(hn);
        System.out.println("根据血量倒排序后的结果:");
        HeroNode ho=new HeroNode();
        for(HeroNode s:hn){
            ho.add(s);
        }
        System.out.println(ho.values());
    }
}

输出:

初始化10个hero:
[[name:hero0, hp=349.0], [name:hero1, hp=619.0], [name:hero2, hp=32.0], [name:hero3, hp=302.0], [name:hero4, hp=914.0], [name:hero5, hp=923.0], [name:hero6, hp=839.0], [name:hero7, hp=241.0], [name:hero8, hp=655.0], [name:hero9, hp=391.0]]
根据血量倒排序后的结果:
[[name:hero5, hp=923.0], [name:hero4, hp=914.0], [name:hero6, hp=839.0], [name:hero8, hp=655.0], [name:hero1, hp=619.0], [name:hero9, hp=391.0], [name:hero0, hp=349.0], [name:hero3, hp=302.0], [name:hero7, hp=241.0], [name:hero2, hp=32.0]]

比较冒泡法,选择法以及二叉树排序的性能区别

创建4万个随机数,然后分别用冒泡法选择法二叉树 3种排序算法进行排序,比较哪种更快。

package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
public class xingneng {
    //创建4万个随机数,分别用冒泡法、选择法、二叉树进行性能比较
    public static void main(String[] args) {
        //生成随机数,复制3个数组,确保数据一样,并且用于对比排序结果是否相同
        int[]I = suiji(40000);
        int[]A = Arrays.copyOf(I, I.length);
        int[]B = Arrays.copyOf(I, I.length);
        int[]C = Arrays.copyOf(I, I.length);
         
        //冒泡法
        long start = System.currentTimeMillis();
        maopao(A);
        long end = System.currentTimeMillis();
        System.out.printf("冒泡法耗时:%d 毫秒 %n",end - start);
         
        //选择法
        start = System.currentTimeMillis();
        xuanze(B);
        end = System.currentTimeMillis();
        System.out.printf("选择法耗时:%d 毫秒 %n",end - start);
         
        //二叉树
        start = System.currentTimeMillis();
        tree(C);
        end = System.currentTimeMillis();
        System.out.printf("二叉树排序法耗时:%d 毫秒 %n",end - start);
         
        //少量数据对比结果(可视化)
		//System.out.println("源数据:");
		//for(int i : I)
		//System.out.print(i+" ");
		//System.out.println();
		//      
		//System.out.println("冒泡法:");
		//for(int a : A)
		//System.out.print(a+" ");
		//System.out.println();
		//      
		//System.out.println("选择法:");
		//for(int b : B)
		//System.out.print(b+" ");
		//System.out.println();
		//      
		//System.out.println("二叉树:");
		//for(Object c : C)
		//System.out.print(c+" ");
		//System.out.println();
         
        //大量数据对比结果(不可视化)
        System.out.println("冒泡法与选择法排序数据对比:");
        System.out.println(Arrays.equals(A, B));
        System.out.println("选择法与二叉树排序数据对比:");
        System.out.println(Arrays.equals(B, C));
        System.out.println("二叉树与冒泡法排序数据对比:");
        System.out.println(Arrays.equals(C, A));
    }
    /**
     *  需要生成的随机数个数
     *  随机数范围[1,101)
     */
    public static int[] suiji(int number){
        int[] num = new int[number];
        for (int i = 0; i < number; i++) {
            num[i] = (int)(1+Math.random()*100);
        }
        return num;
    }
     
    //冒泡法排序
    public static void maopao(int[] A){
        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < A.length - i - 1; j++) {
                if(A[j] > A[j+1]){
                    int temp = A[j];
                    A[j] = A[j+1];
                    A[j+1] = temp;
                }
            }
        }
    }
     
    //选择法排序
    public static void xuanze(int[] A){
        for (int i = 0; i < A.length - 1; i++) {
            for (int j = i + 1; j < A.length; j++) {
                if(A[j] < A[i]){
                    int temp = A[i];
                    A[i] = A[j];
                    A[j] = temp;
                }
            }
        }
    }
     
    //二叉树排序
    public xingneng left;//左子节点
    public xingneng right;//右子节点
    public Object value;//节点数据
     
    //添加数据
    public void add(Object A){
        if(null == value){
            value = A;
        }
        else{
            //如果新加入的数据小于左节点数据
            if((Integer)A -(Integer)value <= 0){
                if(null == left)
                    left = new xingneng();
                left.add(A);
            }
            else{
                if(null == right)
                    right = new xingneng();
                right.add(A);
            }
        }
    }
     
    //中序遍历所有节点
    public List<Object> values(){
        List<Object> values = new ArrayList<>();
        //遍历左节点
        if(null != left)
            values.addAll(left.values());
        //当前节点
         values.add(value);
        //遍历右节点
         if(null != right)
             values.addAll(right.values());
        return values;
    }
     
    public static void tree(int[] A){
        xingneng roots = new xingneng();
        //将数据加入节点排序
        for(int number : A){
            roots.add(number);
        }
        //中序遍历节点
        List<Object> T = roots.values();
        for (int i = 0; i < T.size(); i++) {
            A[i] = (int) T.get(i);
        }
    }
}

在这里插入图片描述
3.HashMap

HashMap的键值对

HashMap储存数据的方式是—— 键值对

package collection;
import java.util.HashMap; 
public class TestCollection {
    public static void main(String[] args) {
        HashMap<String,String> dictionary = new HashMap<>();
        dictionary.put("adc", "物理英雄");
        dictionary.put("apc", "魔法英雄");
        dictionary.put("t", "坦克");
         
        System.out.println(dictionary.get("t"));
    }
}

HashMap遍历的四种方法

//第一种:普遍使用,二次取值
 System.out.println("通过Map.keySet遍历key和value:");
 for (String key : map.keySet()) {
  System.out.println("key= "+ key + " and value= " + map.get(key));
 }
  
 //第二种
 System.out.println("通过Map.entrySet使用iterator遍历key和value:");
 Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
 while (it.hasNext()) {
  Map.Entry<String, String> entry = it.next();
  System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
 }
  
 //第三种:推荐,尤其是容量大时
 System.out.println("通过Map.entrySet遍历key和value");
 for (Map.Entry<String, String> entry : map.entrySet()) {
  System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
 }
 
 //第四种
 System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
 for (String v : map.values()) {
  System.out.println("value= " + v);
 }

键不能重复,值可以重复

对于HashMap而言,key是唯一的,不可以重复的。
所以,以相同的key 把不同的value插入到 Map中会导致旧元素被覆盖,只留下最后插入的元素。
不过,同一个对象可以作为值插入到map中,只要对应的key不一样。

package collection;
import java.util.HashMap;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        HashMap<String,Hero> heroMap = new HashMap<String,Hero>();
         
        heroMap.put("gareen", new Hero("gareen1"));
        System.out.println(heroMap);
         
        //key为gareen已经有value了,再以gareen作为key放入数据,会导致原英雄,被覆盖
        //不会增加新的元素到Map中
        heroMap.put("gareen", new Hero("gareen2"));
        System.out.println(heroMap);
         
        //清空map
        heroMap.clear();
        Hero gareen = new Hero("gareen");
         
        //同一个对象可以作为值插入到map中,只要对应的key不一样
        heroMap.put("hero1", gareen);
        heroMap.put("hero2", gareen);
         
        System.out.println(heroMap);    
    }
}

在这里插入图片描述
练习-查找内容性能比较

准备一个ArrayList其中存放3000000(三百万个)Hero对象,其名称是随机的,格式是hero-[4位随机数]
hero-3229
hero-6232
hero-9365

因为总数很大,所以几乎每种都有重复,把名字叫做 hero-5555的所有对象找出来
要求使用两种办法来寻找

  1. 不使用HashMap,直接使用for循环找出来,并统计花费的时间
  2. 借助HashMap,找出结果,并统计花费的时间
package collection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> hs =new ArrayList<>();
        System.out.println("初始化开始");
        for (int i = 0; i < 3000000; i++) {
            Hero h = new Hero(   "hero-" + random());
            hs.add(h);
        }
        //名字作为key
        //名字相同的hero,放在一个List中,作为value
        HashMap<String,List<Hero>> heroMap =new HashMap();
        for (Hero h : hs) { 
           //初次为空,创建对象存入heroMap,再重新判断有无该名字的,有直接add,其他类同                                                                                                                          		 
            List<Hero> list= heroMap.get( h.name);
            if(list==null){
                list = new ArrayList<>();
                heroMap.put(h.name, list);
            }
            list.add(h);
        }
          
        System.out.println("初始化结束");
        System.out.println("开始查找");
        findByIteration(hs);
        findByMap(heroMap);
          
    }
    private static List<Hero> findByMap(HashMap<String,List<Hero>> m) {
        long start =System.currentTimeMillis();
        List <Hero>result= m.get("hero-5555");
        long end =System.currentTimeMillis();
        System.out.printf("通过map查找,一共找到%d个英雄,耗时%d 毫秒%n",result.size(),end-start);
        return result;
    }
    private static List<Hero> findByIteration (List<Hero> hs) {
        long start =System.currentTimeMillis();
        List<Hero> result =new ArrayList<>();
        for (Hero h : hs) {
            if(h.name.equals("hero-5555")){
                result.add(h);
            }
        }
        long end =System.currentTimeMillis();
        System.out.printf("通过for查找,一共找到%d个英雄,耗时%d 毫秒%n", result.size(),end-start);
        return result;
    }
    public static int random(){
        return ((int)(Math.random()*9000)+1000);
    }
}

在这里插入图片描述
需要注意的是:
Map的key是字符串,英雄的名称
Map的value是List,里面放了名称相同的多个英雄
如图所示,使用Map查找会快非常多,涉及到hashcode 原理。

4.HashSet

元素不能重复

Set中的元素,不能重复

package collection;
import java.util.HashSet;
public class TestCollection {
    public static void main(String[] args) { 
        HashSet<String> names = new HashSet<String>();
        names.add("gareen"); 
        System.out.println(names);
         
        //第二次插入同样的数据,是插不进去的,容器中只会保留一个
        names.add("gareen");//添加已有元素返回false
        names.add("teemo");
        System.out.println(names);//没有顺序
    }
}

在这里插入图片描述
没有顺序

Set中的元素,没有顺序。
严格的说,是没有按照元素的插入顺序排列。

HashSet的具体顺序,既不是按照插入顺序,也不是按照hashcode的顺序。

以下是HashSet源代码中的部分注释
/**

  • It makes no guarantees as to the iteration order of the set;
  • in particular, it does not guarantee that the order will remain constant over time.
    */

不保证Set的迭代顺序; 确切的说,在不同条件下,元素的顺序都有可能不一样

换句话说,同样是插入0-9到HashSet中, 在JVM的不同版本中,看到的顺序都是不一样的。 所以在开发的时候,不能依赖于某种臆测的顺序,这个顺序本身是不稳定的。

package collection;
import java.util.HashSet;
public class TestCollection {
    public static void main(String[] args) {
        HashSet<Integer> numbers = new HashSet<Integer>();
        numbers.add(9);
        numbers.add(5);
        numbers.add(1);
 
        // Set中的元素排列,不是按照插入顺序
        System.out.println(numbers);
    }
}

在这里插入图片描述
遍历

Set不提供get()来获取指定位置的元素
所以遍历需要用到迭代器,或者增强型for循环

package collection;
import java.util.HashSet;
import java.util.Iterator;
public class TestCollection {
    public static void main(String[] args) {
        HashSet<Integer> numbers = new HashSet<Integer>();
         
        for (int i = 0; i < 20; i++) {
            numbers.add(i);
        }
         
        //Set不提供get方法来获取指定位置的元素
        //numbers.get(0)
         
        //遍历Set可以采用迭代器iterator
        for (Iterator<Integer> iterator = numbers.iterator(); iterator.hasNext();) {
            Integer i = (Integer) iterator.next();
            System.out.println(i);
        }
         
        //或者采用增强型for循环
        for (Integer i : numbers) {
            System.out.println(i);
        }
    }
}

HashSet和HashMap的关系

通过观察HashSet的源代码
可以发现HashSet自身并没有独立的实现,而是在里面封装了一个Map.
HashSet是作为Map的key而存在的
value是一个命名为PRESENT的static的Object对象,因为是一个类属性,所以只会有一个。
private static final Object PRESENT = new Object();

package collection;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    //HashSet里封装了一个HashMap
    private  HashMap<E,Object> map;
 
    private static final Object PRESENT = new Object();
 
    //HashSet的构造方法初始化这个HashMap
    public HashSet() {
        map = new HashMap<E,Object>();
    }
 
    //向HashSet中增加元素,其实就是把该元素作为key,增加到Map中
    //value是PRESENT,静态,final的对象,所有的HashSet都使用这么同一个对象
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
 
    //HashSet的size就是map的size
    public int size() {
        return map.size();
    }
 
    //清空Set就是清空Map
    public void clear() {
        map.clear();
    }
     
    //迭代Set,就是把Map的键拿出来迭代
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
}

练习-HashSet

在比较字符串章节,有一个同样的练习
创建一个长度是100的字符串数组
使用长度是2的随机字符填充该字符串数组
统计这个字符串数组里重复的字符串有多少种
使用HashSet来解决这个问题
思路

HashSet<String> randomchar = new HashSet<String>();
HashSet<String> randomchar1 = new HashSet<String>();
if(randomchar.add(temp)==false) {//如果已经存在了temp,返回false,false==false
                randomchar1.add(temp);
}

参考

package collection;

import java.util.HashSet;

public class TestString {
     
    public static void main(String[] args) {
        //创建一个长度是100的字符串数组
        //字符串数组排序
        // 在for循环中采用StringBuilder比用String的 `+=`高效
        StringBuilder str1 = new StringBuilder();  //得到字符串
        String[] str3=new String[100];//存放100个长度是2的随机字符串
        StringBuilder str4= new StringBuilder();  
        HashSet<String> randomchar = new HashSet<String>();
        HashSet<String> randomchar1 = new HashSet<String>();
        // 得到字符串
        for (short i = '0'; i <= 'z'; i++) {
            if (Character.isLetter((char) i) || Character.isDigit((char) i)){
                str1.append((char) i);
            }
        }
        String str2= str1.toString();
        System.out.println(str2);
        for(int i = 0; i < 100; i++){
            for(int j=0;j<2;j++){
                int idx =(int) Math.floor(Math.random() * str2.length());
                str4.append(str2.charAt(idx));//str2.charAt(idx);得到一个字符
                str3[i]=str4.toString();
            }
            if(randomchar.add(str3[i])==false) {//如果已经存在了str3[i],返回false,false==false
                randomchar1.add(str3[i]);
            }
            System.out.print(str3[i]+"\t");
            if(i!=0&&(i+1)%10==0){
                System.out.println();
            }
                str4.delete(0, str4.length());//将str4清空
        }
        System.out.println();

        System.out.printf("总共有%d种重复的字符串\n", randomchar1.size());
        System.out.println("分别是:");
        for(String temp:randomchar1) {
            System.out.print(temp+"\t");
        }
    }
}

在这里插入图片描述
5.Collection

定义:Collection是一个接口。

Collection

Collection是 Set List Queue和 Deque的接口
Queue: 先进先出队列
Deque: 双向链表

注:Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
注:Deque 继承 Queue,间接的继承了 Collection
在这里插入图片描述
6.Collections

定义

Collections是一个类,容器的工具类,就如同Arrays是数组的工具类。

方法

reverse 反转:reverse 使List中的数据发生翻转

package collection;  
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        //初始化集合numbers
        List<Integer> numbers = new ArrayList<>();
         
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }
         
        System.out.println("集合中的数据:");
        System.out.println(numbers);
         
        Collections.reverse(numbers);
         
        System.out.println("翻转后集合中的数据:");
        System.out.println(numbers);   
    }
}

在这里插入图片描述
shuffle 混淆:shuffle 混淆List中数据的顺序。

package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        //初始化集合numbers
        List<Integer> numbers = new ArrayList<>();
         
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }
         
        System.out.println("集合中的数据:");
        System.out.println(numbers);
         
        Collections.shuffle(numbers);
         
        System.out.println("混淆后集合中的数据:");
        System.out.println(numbers);    
    }
}

在这里插入图片描述
sort 排序:sort 对List中的数据进行排序。

package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        //初始化集合numbers
        List<Integer> numbers = new ArrayList<>();
         
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }
         
        System.out.println("集合中的数据:");
        System.out.println(numbers);
 
        Collections.shuffle(numbers);
        System.out.println("混淆后集合中的数据:");
        System.out.println(numbers);
 
        Collections.sort(numbers);
        System.out.println("排序后集合中的数据:");
        System.out.println(numbers); 
    }
}

在这里插入图片描述
swap 交换:swap 交换两个数据的位置。

package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        //初始化集合numbers
        List<Integer> numbers = new ArrayList<>(); 
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }
         
        System.out.println("集合中的数据:");
        System.out.println(numbers);
 
        Collections.swap(numbers,0,5);
        System.out.println("交换0和5下标的数据后,集合中的数据:");
        System.out.println(numbers);
    }
}

在这里插入图片描述
rotate 滚动:rotate 把List中的数据,向右滚动指定单位的长度。

package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        //初始化集合numbers
        List<Integer> numbers = new ArrayList<>();
         
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }
         
        System.out.println("集合中的数据:");
        System.out.println(numbers);
 
        Collections.rotate(numbers,2);
        System.out.println("把集合向右滚动2个单位,标的数据后,集合中的数据:");
        System.out.println(numbers); 
    }
}

在这里插入图片描述
synchronizedList 线程安全化:synchronizedList 把非线程安全的List转换为线程安全的List。

package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
 
        System.out.println("把非线程安全的List转换为线程安全的List");
        List<Integer> synchronizedNumbers = (List<Integer>) Collections.synchronizedList(numbers);
 
    }
}

练习-统计概率

首先初始化一个List,长度是10,值是0-9。
然后不断的shuffle,直到前3位出现
3 1 4

shuffle 1000,000 次,统计出现的概率

package collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class TestCollection {
    public static void main(String[] args) { 	
    	List<Integer> numbers=new ArrayList<>();
    	for(int i=0;i<10;i++){
    		numbers.add(i);
    	}
    	System.out.println(numbers);
    	int count=0;
    	for(int i=0;i<1000000;i++){
    		Collections.shuffle(numbers);
    		if(numbers.get(0)==3&&numbers.get(1)==1&&numbers.get(2)==4){
    			count++;
    		}
    	}
    	System.out.println("总共出现"+count+"次");
    	System.out.printf("shuffle 1000,000 次,出现的概率为%f",(double)count/1000000);
    }
}

在这里插入图片描述

五.关系与区别

1.ArrayList vs HashSet

是否有顺序

ArrayList: 有顺序
HashSet: 无顺序

package collection;
   
import java.util.ArrayList;
import java.util.HashSet;
    
public class TestCollection {
    public static void main(String[] args) {
           
        ArrayList<Integer> numberList =new ArrayList<Integer>();
        //List中的数据按照插入顺序存放
        System.out.println("----------List----------");
        System.out.println("向List 中插入 9 5 1");
        numberList.add(9);
        numberList.add(5);
        numberList.add(1);
        System.out.println("List 按照顺序存放数据:");
        System.out.println(numberList);
        System.out.println("----------Set----------");
        HashSet<Integer> numberSet =new HashSet<Integer>();
        System.out.println("向Set 中插入9 5 1");
        //Set中的数据不是按照插入顺序存放
        numberSet.add(9);
        numberSet.add(5);
        numberSet.add(1);
        System.out.println("Set 不是按照顺序存放数据:");
        System.out.println(numberSet);
           
    }
}

在这里插入图片描述
能否重复

List中的数据可以重复
Set中的数据不能够重复
重复判断标准是:
首先看hashcode是否相同
如果hashcode不同,则认为是不同数据
如果hashcode相同,再比较equals,如果equals相同,则是相同数据,否则是不同数据

package collection;
import java.util.ArrayList;
import java.util.HashSet;  
public class TestCollection {
    public static void main(String[] args) {     
        ArrayList<Integer> numberList =new ArrayList<Integer>();
        //List中的数据可以重复
        System.out.println("----------List----------");
        System.out.println("向List 中插入 9 9");
        numberList.add(9);
        numberList.add(9);
        System.out.println("List 中出现两个9:");
        System.out.println(numberList);
        System.out.println("----------Set----------");
        HashSet<Integer> numberSet =new HashSet<Integer>();
        System.out.println("向Set 中插入9 9");
        //Set中的数据不能重复
        numberSet.add(9);
        numberSet.add(9);
        System.out.println("Set 中只会保留一个9:");
        System.out.println(numberSet);      
    }
}

在这里插入图片描述
练习-不重复的随机数

生成50个 0-9999之间的随机数,要求不能有重复的。

package collection;
import java.util.HashSet;
import java.util.Random;
public class TestCollection {
    public static void main(String[] args) { 	
     HashSet<Integer> numberSet =new HashSet<Integer>();
     do{
    	numberSet.add((int)(Math.random()*10000));
     }while(numberSet.size()!=50);	
     System.out.println(numberSet);
    }
}

输出

[6658, 4355, 7812, 4234, 6282, 5389, 6799, 3730, 9363, 4628, 5781, 790, 8856, 1052, 7723, 6448, 5810, 4146, 7860, 7220, 1719, 2364, 1344, 4161, 6465, 6082, 69, 9544, 1226, 3788, 1103, 216, 7768, 7001, 473, 4570, 1755, 9309, 9183, 9568, 8160, 610, 7394, 2666, 5611, 9073, 372, 630, 7543, 5112]

2.ArrayList vs LikedList

ArrayList和LinkedList的区别

ArrayList 插入,删除数据慢
LinkedList, 插入,删除数据快
ArrayList是顺序结构,所以定位很快,指哪找哪。 就像电影院位置一样,有了电影票,一下就找到位置了。
LinkedList 是链表结构,就像手里的一串佛珠,要找出第99个佛珠,必须得一个一个的数过去,所以定位慢。
在这里插入图片描述
插入数据

package collection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        List<Integer> l;
        l = new ArrayList<>();
        insertFirst(l, "ArrayList");
 
        l = new LinkedList<>();
        insertFirst(l, "LinkedList");
 
    }
 
    private static void insertFirst(List<Integer> l, String type) {
        int total = 1000 * 100;
        final int number = 5;
        long start = System.currentTimeMillis();
        for (int i = 0; i < total; i++) {
            l.add(0, number);//每次在首位插入数字5
        }
        long end = System.currentTimeMillis();
        System.out.printf("在%s 最前面插入%d条数据,总共耗时 %d 毫秒 %n", type, total, end - start);
    }
}

在这里插入图片描述
定位数据

package collection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class TestCollection {
    public static void main(String[] args) {
        List<Integer> l;
        l = new ArrayList<>();
        modify(l, "ArrayList");
 
        l = new LinkedList<>();
        modify(l, "LinkedList");
 
    }
 
    private static void modify(List<Integer> l, String type) {
        int total = 100 * 1000;
        int index = total/2;
        final int number = 5;
        //初始化
        for (int i = 0; i < total; i++) {
            l.add(number);
        }
         
        long start = System.currentTimeMillis();
 
        for (int i = 0; i < total; i++) {
             int n = l.get(index);
             n++;
             l.set(index, n);//定位index每次替换成n这个值
        }
        long end = System.currentTimeMillis();
        System.out.printf("%s总长度是%d,定位到第%d个数据,取出来,加1,再放回去%n 重复%d遍,总共耗时 %d 毫秒 %n", type,total, index,total, end - start);
        System.out.println();
    }
 
}

在这里插入图片描述
练习

在后面插入数据:比较 ArrayList和LinkedList 在最后面插入100000条数据,谁快谁慢?为什么?
在中间插入数据:在List的中间位置,插入数据,比较ArrayList快,还是LinkedList快,并解释为什么?

package collection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class TestCollection {
    private static void insertBackwards(List<Integer>l,String type) {
        int total=100000;
        final int number=5;
         
        long starttime=System.currentTimeMillis();
        for(int i=0;i<total;i++) {
            l.add(number);;
        }
        long endtime=System.currentTimeMillis();
        long time=endtime-starttime;
        System.out.println("使用"+type+"进行在后面插入数据的所用时间为:"+time+"ms");
    }
     
    private static void insertForwards(List<Integer>l,String type) {
        int total=100000;
        final int number=5;
        long starttime=System.currentTimeMillis();
        for(int i=0;i<total;i++) {
            l.add(i/2, number);//若total为奇数,需要重新进行一些处理
        }
        long endtime=System.currentTimeMillis();
        long time=endtime-starttime;
        System.out.println("使用"+type+"进行在中间插入数据的所用时间为:"+time+"ms");
    }
    public static void main(String[] args) {
        List<Integer> l;
        l=new ArrayList<>();
        insertBackwards(l, "ArrayList");
        l=new LinkedList<>();
        insertBackwards(l, "LinkedList");
        l=new ArrayList<>();
        insertForwards(l, "ArrayList");
        l=new LinkedList<>();
        insertForwards(l, "LinkedList");
    }
}

在这里插入图片描述
3.HashMap vs HashTable

HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
HashMap可以存放 null
Hashtable不能存放null
区别2:
HashMap不是线程安全的类
Hashtable是线程安全的类

package collection;
import java.util.HashMap;
import java.util.Hashtable;
public class TestCollection {
    public static void main(String[] args) {
         
        //HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
         
        HashMap<String,String> hashMap = new HashMap<String,String>();
         
        //HashMap可以用null作key,作value
        hashMap.put(null, "123");
        hashMap.put("123", null);
         
        Hashtable<String,String> hashtable = new Hashtable<String,String>();
        //Hashtable不能用null作key,不能用null作value
        hashtable.put(null, "123");
        hashtable.put("123", null);
    }
}

练习-反转key和value

使用如下键值对,初始化一个HashMap:
adc - 物理英雄
apc - 魔法英雄
t - 坦克

对这个HashMap进行反转,key变成value,value变成key
提示: keySet()可以获取所有的key, values()可以获取所有的value

思路:新建一个hashmap,然后把新hashmap覆盖

package collection;
import java.util.HashMap;
public class TestCollection {
    public static void main(String[] args) { 	
    	HashMap<String,String> hashMap = new HashMap<String,String>();
    	HashMap<String,String> map = new HashMap<String,String>();
    	hashMap.put("abc" , " 物理英雄");
        hashMap.put("apc", "魔法英雄");
        hashMap.put("t", "坦克");
        System.out.println(hashMap);
        System.out.println("通过hashMap.keySet遍历key和value:");
        for (String key : hashMap.keySet()) {
         System.out.println("key= "+ key + " and value= " + hashMap.get(key));
         map.put(hashMap.get(key) , key);
        }
        hashMap=map;
        System.out.println("对这个HashMap进行反转:");
        System.out.println(hashMap); 
    }
}

在这里插入图片描述
注:
HashMap的数据排列不是按照插入顺序,也不是按照大小/名称顺序,而是按照key的HashCode顺序,key改变了顺序自然改变了。

4.几种Set

HashSet LinkedHashSet TreeSet

HashSet: 无序
LinkedHashSet: 按照插入顺序
TreeSet: 从小到大排序

package collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
public class TestCollection {
    public static void main(String[] args) {
        HashSet<Integer> numberSet1 =new HashSet<Integer>();
        //HashSet中的数据不是按照插入顺序存放
        numberSet1.add(88);
        numberSet1.add(8);
        numberSet1.add(888);
        numberSet1.add(1);
        numberSet1.add(2);
        numberSet1.add(3);
        
        System.out.println(numberSet1);
          
        LinkedHashSet<Integer> numberSet2 =new LinkedHashSet<Integer>();
        //LinkedHashSet中的数据是按照插入顺序存放
        numberSet2.add(88);
        numberSet2.add(8);
        numberSet2.add(888);
        numberSet2.add(1);
        numberSet2.add(2);
        numberSet2.add(3);
          
        System.out.println(numberSet2);
        TreeSet<Integer> numberSet3 =new TreeSet<Integer>();
        //TreeSet 中的数据是进行了排序的
        numberSet3.add(88);
        numberSet3.add(8);
        numberSet3.add(888);
        numberSet3.add(1);
        numberSet3.add(2);
        numberSet3.add(3);
          
        System.out.println(numberSet3);    
    }
}

在这里插入图片描述
练习-既不重复,又有顺序

利用LinkedHashSet的既不重复,又有顺序的特性,把Math.PI中的数字,按照出现顺序打印出来,相同数字,只出现一次。

package collection;
import java.util.LinkedHashSet;
public class TestCollection {
    public static void main(String[] args) {
        String a=String.valueOf(Math.PI);
        a=a.replace(".", "");   //a.replace(".","")不会改变a
        LinkedHashSet<Character>l=new LinkedHashSet<>();
        for(char c:a.toCharArray()) {
        	//方法二:if(c=='.') continue;
            l.add(c);
        }
        System.out.println("得到结果为:"+l);
    }
}

在这里插入图片描述

六.其他

1.hashcode原理

List查找的低效率

假设在List中存放着无重复名称,没有顺序的2000000个Hero
要把名字叫做“hero 1000000”的对象找出来
List的做法是对每一个进行挨个遍历,直到找到名字叫做“hero 1000000”的英雄。
最差的情况下,需要遍历和比较2000000次,才能找到对应的英雄。
测试逻辑:

  1. 初始化2000000个对象到ArrayList中
  2. 打乱容器中的数据顺序
  3. 进行10次查询,统计每一次消耗的时间
    不同计算机的配置情况下,所花的时间是有区别的。
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; 
import charactor.Hero; 
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> heros = new ArrayList<Hero>();
            
        for (int j = 0; j < 2000000; j++) {
            Hero h = new Hero("Hero " + j);
            heros.add(h);
        }
            
        // 进行10次查找,观察大体的平均值
        for (int i = 0; i < 10; i++) {
            // 打乱heros中元素的顺序
            Collections.shuffle(heros);
             
            long start = System.currentTimeMillis();
     
            String target = "Hero 1000000";
     
            for (Hero hero : heros) {
                if (hero.name.equals(target)) {
                    System.out.println("找到了 hero!" );
                    break;
                }
            }
            long end = System.currentTimeMillis();
            long elapsed = end - start;
            System.out.println("一共花了:" + elapsed + " 毫秒");
        }     
    }
}

在这里插入图片描述
HashMap的性能表现

使用HashMap 做同样的查找

  1. 初始化2000000个对象到HashMap中。
  2. 进行10次查询
  3. 统计每一次的查询消耗的时间

可以观察到,几乎不花时间,花费的时间在1毫秒以内

package collection;
import java.util.HashMap;
import charactor.Hero;
public class TestCollection {
    public static void main(String[] args) {
        HashMap<String,Hero> heroMap = new HashMap<String,Hero>();
        for (int j = 0; j < 2000000; j++) {
            Hero h = new Hero("Hero " + j);
            heroMap.put(h.name, h);
        }
        System.out.println("数据准备完成");
  
        for (int i = 0; i < 10; i++) {
            long start = System.currentTimeMillis();
              
            //查找名字是Hero 1000000的对象
            Hero target = heroMap.get("Hero 1000000");
            System.out.println("找到了 hero!" + target.name);
              
            long end = System.currentTimeMillis();
            long elapsed = end - start;
            System.out.println("一共花了:" + elapsed + " 毫秒");
        }
    }
}

在这里插入图片描述
HashMap原理与字典

在展开HashMap原理的讲解之前,首先回忆一下大家初中和高中使用的汉英字典。
比如要找一个单词对应的中文意思,假设单词是Lengendary,首先在目录找到Lengendary在第 555页。
然后,翻到第555页,这页不只一个单词,但是量已经很少了,逐一比较,很快就定位目标单词Lengendary。
555相当于就是Lengendary对应的hashcode

分析HashMap性能卓越的原因

-----hashcode概念-----
所有的对象,都有一个对应的hashcode(散列值)
比如字符串“gareen”对应的是1001 (实际上不是,这里是方便理解,假设的值)
比如字符串“temoo”对应的是1004
比如字符串“db”对应的是1008
比如字符串“annie”对应的也是1008

-----保存数据-----
准备一个数组,其长度是2000,并且设定特殊的hashcode算法,使得所有字符串对应的hashcode,都会落在0-1999之间
要存放名字是"gareen"的英雄,就把该英雄和名称组成一个键值对,存放在数组的1001这个位置上
要存放名字是"temoo"的英雄,就把该英雄存放在数组的1004这个位置上
要存放名字是"db"的英雄,就把该英雄存放在数组的1008这个位置上
要存放名字是"annie"的英雄,然而 "annie"的hashcode 1008对应的位置已经有db英雄了,那么就在这里创建一个链表,接在db英雄后面存放annie

-----查找数据-----
比如要查找gareen,首先计算"gareen"的hashcode是1001,根据1001这个下标,到数组中进行定位,(根据数组下标进行定位,是非常快速的) 发现1001这个位置就只有一个英雄,那么该英雄就是gareen.
比如要查找annie,首先计算"annie"的hashcode是1008,根据1008这个下标,到数组中进行定位,发现1008这个位置有两个英雄,那么就对两个英雄的名字进行逐一比较(equals),因为此时需要比较的量就已经少很多了,很快也就可以找出目标英雄
这就是使用hashmap进行查询,非常快原理。

这是一种用空间换时间的思维方式
总结:即找到数组定位下标,进行内容比较(同个hashcode有不同内容),取出目标内容。
在这里插入图片描述
HashSet判断是否重复

HashSet的数据是不能重复的,相同数据不能保存在一起,到底如何判断是否是重复的呢?
根据HashSet和HashMap的关系,我们了解到因为HashSet没有自身的实现,而是里面封装了一个HashMap,所以本质上就是判断HashMap的key是否重复。
再通过上一步的学习,key是否重复,是由两个步骤判断的:
hashcode是否一样
如果hashcode不一样,就是在不同的坑里,一定是不重复的
如果hashcode一样,就是在同一个坑里,还需要进行equals比较
如果equals一样,则是重复数据
如果equals不一样,则是不同数据。

练习

自定义字符串的hashcode

如下是Java API提供的String的hashcode生成办法;
s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

s[0] 表示第一位字符
n表示字符串的长度
本练习并不是要求去理解这个算法,而是自定义一个简单的hashcode算法,计算任意字符串的hashcode
因为String类不能被重写,所以我们通过一个静态方法来返回一个String的hashcode

public static int hashcode(String)

如果字符串长度是0,则返回0。
否则: 获取每一位字符,转换成数字后,相加,最后乘以23

(s[0]+ s[1] + s[2] + s[3]+ s[n-1])*23.

如果值超过了1999,则取2000的余数,保证落在0-1999之间。
如果是负数,则取绝对值。

随机生成长度是2-10的不等的100个字符串,打印用本hashcode获取的值分别是多少

package collection;
import java.util.Scanner;
public class TestCollection {
    public static int hashcode(String str) {
        if(str.length()==0) {
            return 0;
        }
        char[]c=str.toCharArray();
        int sum=0;
        for(int i=0;i<c.length;i++) {
            sum+=(char)c[i];
        }
        int hashcode=sum*23;
        if(hashcode<0){
    		hashcode=-hashcode;
        }
        if(hashcode>1999) {
            hashcode=hashcode%2000;
        }
        return hashcode;
    }
 
    public static void main(String[] args) {
        // TODO 自动生成的方法存根
        System.out.println("请输入字符串:");
        Scanner s=new Scanner(System.in);
        String a=s.nextLine();      
        System.out.println("得到的hashcode为:");
        System.out.println(hashcode(a));
    }
}

在这里插入图片描述
自定义MyHashMap

根据前面学习的hashcode的原理和自定义hashcode, 设计一个MyHashMap,实现接口IHashMap

MyHashMap内部由一个长度是2000的对象数组实现。

设计put(String key,Object value)方法
首先通过上一个自定义字符串的hashcode练习获取到该字符串的hashcode,然后把这个hashcode作为下标,定位到数组的指定位置。
如果该位置没有数据,则把字符串和对象组合成键值对Entry,再创建一个LinkedList,把键值对,放进LinkedList中,最后把LinkedList 保存在这个位置。
如果该位置有数据,一定是一个LinkedList,则把字符串和对象组合成键值对Entry,插入到LinkedList后面。

设计 Object get(String key) 方法
首先通过上一个自定义字符串的hashcode练习获取到该字符串的hashcode,然后把这个hashcode作为下标,定位到数组的指定位置。
如果这个位置没有数据,则返回空
如果这个位置有数据,则挨个比较其中键值对的键-字符串,是否equals,找到匹配的,把键值对的值,返回出去。找不到匹配的,就返回空

package collection;
public interface IHashMap {
    public void put(String key,Object object);
    public Object get(String key);
}
package collection;
//键值对
package collection;
//键值对
public class Entry {
    public Entry(Object key, Object value) {
        super();
        this.key = key;
        this.value = value;
    }
    public Object key;
    public Object value;
    @Override
    public String toString() {
        return "[key=" + key + ", value=" + value + "]";
    }  
}
package collection;
import java.util.LinkedList;
public class MyHashMap implements IHashMap{
    private Object[] objects=new Object[2000];

    public MyHashMap(){
    }

    @Override
    public void put(String key, Object value) {
        int hashCode=hashcode(key);
        if(objects[hashCode]==null){
        	//key第一次出现
            LinkedList<Entry> list=new LinkedList<>();
            list.add(new Entry(key,value));
            objects[hashCode]=list;
        }else{
            LinkedList<Entry> list=(LinkedList<Entry>)objects[hashCode];  
            boolean flag=true;
            for(Entry i:list){
            	//key重复,value覆盖
                if(i.key.equals(key)){
                    i.value=value;
                    flag=!flag;
                    break;
                }
            }
          //一个数组多个值,用key,value存放到链表中
            if(flag){
            	list.add(new Entry(key,value));
                objects[hashCode]=list;
            }   
        }
    }

    @Override
    public Object get(String key) {
        int hashCode=hashcode(key);
        Object object=objects[hashCode];
        if(object!=null){
            LinkedList<Entry> list=(LinkedList<Entry>)object;
            for (Entry e:list){
                if(e.key.equals(key)){
                    return e.value;
                }
            }
        }
        return null;
    }
    
    public static int hashcode(String str) {
        if(str.length()==0) {
            return 0;
        }
        char[]c=str.toCharArray();
        int sum=0;
        for(int i=0;i<c.length;i++) {
            sum+=(char)c[i];
        }
        int hashcode=sum*23;
        if(hashcode<0){
    		hashcode=-hashcode;
        }
        if(hashcode>1999) {
            hashcode=hashcode%2000;
        }
        return hashcode;
    }
    
    public static void main(String[] args) {
    	MyHashMap myHashMap = new MyHashMap();
		for(int i = 0; i < 5; i++) {
			myHashMap.put(""+i, new String("String "+i));
		}
		//key相同,value值覆盖
		myHashMap.put(""+1, new String("String 11"));
		myHashMap.put(""+1, new String("String 111"));
		for(int i = 0; i < 5;i++) {
			System.out.println(myHashMap.get(""+i));
		}
	}
}

在这里插入图片描述
内容查找性能比较

使用HashMap,而是使用上个练习中自定义的MyHashMap.

准备一个ArrayList其中存放100000(十万个)Hero对象,其名称是随机的,格式是hero-[4位随机数]
hero-3229
hero-6232
hero-9365

因为总数很大,所以几乎每种都有重复,把名字叫做 hero-5555的所有对象找出来
要求使用两种办法来寻找

  1. 不使用MyHashMap,直接使用for循环找出来,并统计花费的时间
  2. 借助MyHashMap,找出结果,并统计花费的时间
package charactor;
public class Hero  {
    public String name;
    public float hp;
    public int damage;
  
    public Hero() {
  
    }
    public Hero(String name) {
 
        this.name = name;
    }
    public String toString() {
        return "Hero [name=" + name + "]\r\n";
    }
}
package collection;  
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
  
import charactor.Hero;
  
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> hs =new ArrayList<>();
        System.out.println("初始化开始");
        for (int i = 0; i < 100000; i++) {
            Hero h = new Hero(   "hero-" + random());
            hs.add(h);
        }
          
        System.out.println("初始化结束");
        System.out.println("开始查找");
        findByIteration(hs);
        findByMyHashMap("hero-5555",hs);
          
    }
    private static void findByMyHashMap(String hero,List<Hero> hs) {
    	MyHashMap map = new MyHashMap();
    	//名字作为key
        //名字相同的hero,放在一个List中,作为value
    	for (Hero h : hs) {         
            List<Hero> hr =(List<Hero>) map.get(h.name);
            if (hr==null) {
                hr = new ArrayList<Hero>();
                map.put(h.name, hr);//hr此时为空,通过后面hr.add(h)可以修改value值
            }
            	hr.add(h);         
        }
    	long start =System.currentTimeMillis();
        if(map.get(hero)==null){
        	 long end =System.currentTimeMillis();
        	 System.out.printf("通过map查找,一共找到0个英雄,耗时%d 毫秒%n",end-start);
        }else{
        	start =System.currentTimeMillis();
        	List<Object> result =  (List<Object>) map.get(hero);
            long end =System.currentTimeMillis();
            System.out.printf("通过map查找,一共找到%d个英雄,耗时%d 毫秒%n",result.size(),end-start);
        }
    }
    private static List<Hero> findByIteration (List<Hero> hs) {
        long start =System.currentTimeMillis();
        List<Hero> result =new ArrayList<>();
        for (Hero h : hs) {
            if(h.name.equals("hero-5555")){
                result.add(h);
            }
        }
        long end =System.currentTimeMillis();
        System.out.printf("通过for查找,一共找到%d个英雄,耗时%d 毫秒%n", result.size(),end-start);
        return result;
    }
    public static int random(){
        return ((int)(Math.random()*9000)+1000);
    }
}

在这里插入图片描述
2.比较器

Comparator

假设Hero有三个属性 name,hp,damage
一个集合中放存放10个Hero,通过Collections.sort对这10个进行排序
那么到底是hp小的放前面?还是damage小的放前面?Collections.sort也无法确定
所以
要指定到底按照哪种属性进行排序

这里就需要提供一个Comparator给定如何进行两个对象之间的大小比较

package charactor;
public class Hero  {
    public String name;
    public float hp;
  
    public int damage;
  
    public Hero() {
  
    }
    public Hero(String name) {
 
        this.name = name;
    }
  
    public String toString() {
        return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]\r\n";
    }
    public Hero(String name, int hp, int damage) {
        this.name = name;
        this.hp = hp;
        this.damage = damage;
    }
}
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import charactor.Hero; 
public class TestCollection {
    public static void main(String[] args) {
        Random r =new Random();
        List<Hero> heros = new ArrayList<Hero>();
            
        for (int i = 0; i < 10; i++) {
            //通过随机值实例化hero的hp和damage
            heros.add(new Hero("hero "+ i, r.nextInt(100), r.nextInt(100)));
        }
        System.out.println("初始化后的集合:");
        System.out.println(heros);
            
        //直接调用sort会出现编译错误,因为Hero有各种属性
        //到底按照哪种属性进行比较,Collections也不知道,不确定,所以没法排
        //Collections.sort(heros);
            
        //引入Comparator,指定比较的算法
        Comparator<Hero> c = new Comparator<Hero>() {
            @Override
            public int compare(Hero h1, Hero h2) {
                //按照hp进行排序
                if(h1.hp>=h2.hp)
                    return 1;  //正数表示h1比h2要大
                else
                    return -1;
            }
        };
        Collections.sort(heros,c);
        System.out.println("按照血量排序后的集合:");
        System.out.println(heros);
    }
}

在这里插入图片描述
Comparable

使Hero类实现Comparable接口
在类里面提供比较算法
Collections.sort就有足够的信息进行排序了,也无需额外提供比较器Comparator
注: 如果返回-1, 就表示当前的更小,否则就是更大

package charactor;   
public class Hero implements Comparable<Hero>{
    public String name;
    public float hp;
       
    public int damage;
       
    public Hero(){
          
    }
      
    public Hero(String name) {
        this.name =name;
  
    }
      
    //初始化name,hp,damage的构造方法
    public Hero(String name,float hp, int damage) {
        this.name =name;
        this.hp = hp;
        this.damage = damage;
    }
  
    @Override
    public int compareTo(Hero anotherHero) {
        if(damage<anotherHero.damage)
            return 1; 
        else
            return -1;
    }
  
    @Override
    public String toString() {
        return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]\r\n";
    }    
}
package collection;
   
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
  
import charactor.Hero;
   
public class TestCollection {
    public static void main(String[] args) {
        Random r =new Random();
        List<Hero> heros = new ArrayList<Hero>();
          
        for (int i = 0; i < 10; i++) {
            //通过随机值实例化hero的hp和damage
            heros.add(new Hero("hero "+ i, r.nextInt(100), r.nextInt(100)));
        }
          
        System.out.println("初始化后的集合");
        System.out.println(heros);
          
        //Hero类实现了接口Comparable,即自带比较信息。
        //Collections直接进行排序,无需额外的Comparator
        Collections.sort(heros);
        System.out.println("按照伤害高低排序后的集合");
        System.out.println(heros);
          
    }
}

在这里插入图片描述
练习

自定义顺序的TreeSet

默认情况下,TreeSet中的数据是从小到大排序的,不过TreeSet的构造方法支持传入一个Comparator
public TreeSet(Comparator comparator)
通过这个构造方法创建一个TreeSet,使得其中的的数字是倒排序的

package collection;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {
	public static void main(String[] args) {
        
        Comparator<Integer> c =new Comparator<Integer>() {
 
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        };
         
        Set<Integer> treeSet = new TreeSet<>(c);
        for (int i = 0; i < 10; i++) {
            treeSet.add(i);
        }
        System.out.println(treeSet);
    }
}

在这里插入图片描述
Comparable

借助Comparable接口,使Item具备按照价格从高到低排序。
初始化10个Item,并且用Collections.sort进行排序,查看排序结果

package charactor;
public class Item implements Comparable<Item>{
    String name;
    int price;
    public Item(){
    	
    }
    public Item(String name,int price){
    	this.name = name;
    	this.price = price;
    }
    
    @Override
    public int compareTo(Item anotheritem) {
        if(price>anotheritem.price)
            return 1; 
        else
            return -1;
    }

	@Override
	public String toString() {
		return "Item [name=" + name + ", price=" + price + "]\r\n";
	}
}
package charactor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class TestCollection {
	public static void main(String[] args) {
        Random r =new Random();
        List<Item> items = new ArrayList<Item>();
          
        for (int i = 0; i < 10; i++) {
            items.add(new Item("item "+ i, r.nextInt(100)));
        }
          
        System.out.println("初始化后的集合");
        System.out.println(items);
          
        //Item类实现了接口Comparable,即自带比较信息。
        //Collections直接进行排序,无需额外的Comparator
        Collections.sort(items);
        System.out.println("按照伤害高低排序后的集合");
        System.out.println(items);   
    }
}

在这里插入图片描述
3.聚合操作

JDK8之后,引入了对集合的聚合操作,可以非常容易的遍历,筛选,比较集合中的元素。

像这样:
String name =heros
.stream()
.sorted((h1,h2)->h1.hp>h2.hp?-1:1)
.skip(2)
.map(h->h.getName())
.findFirst()
.get();

但是要用好聚合,必须先掌握Lambda表达式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值