Java集合框架与泛型

ArrayList

与数组的区别

  • 使用数组的局限性
    如果要存放多个对象,可以使用数组,但是数组有局限性
    比如 声明长度是10的数组
    不用的数组就浪费了,超过10的个数,又放不下
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,容器的容量会随着对象的增加自动增长
    只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
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());  
    } 
}

常用方法

  • 增加
    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.toString()); 
    }
}

  • 判断是否存在
    通过方法 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 获取指定位置的对象,如果输入的下标越界,一样会报错
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.get(5));
        //如果超出了范围,依然会报错
        System.out.println(heros.get(6));
    }
}

  • 获取对象所处的位置
    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用于把对象从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用于替换指定位置的元素
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 用于获取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可以把一个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);
    }
}

  • 把另一个容器所有对象都加进来
    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 清空一个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);
    }
}

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都实现了。

泛型

  • 泛型 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<Hero> genericheros = new ArrayList<Hero>();
    不过JDK7提供了一个可以略微减少代码量的泛型简写方式
    List<Hero> 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<>();      
    }       
}

遍历

  • 用for循环遍历
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); //获得第i个英雄对象
            System.out.println(h);
        } 
    }
}
  • 迭代器遍历
    迭代器有一个空的头部,直接hasNext的就是第一个
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循环可以非常方便的遍历ArrayList中的元素,这是很多开发人员的首选。
    不过增强型for循环也有不足:
    1.无法用来进行ArrayList的初始化
    2.无法得知当前是第几个元素了,当需要只打印单数元素的时候,就做不到了,必须再自定下标变量。
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);
        } 
    } 
}

其他集合

LinkedList

  • LinkedList 与 List接口
    与ArrayList一样,LinkedList也实现了List接口,诸如add,remove,contains等等方法,在此不作赘述,接下来要讲的是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栈
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 charactor.Hero;
   
public class MyStack implements Stack{
   
    LinkedList<Hero> heros = new LinkedList<Hero>();
       
    @Override
    public void push(Hero h) {
        heros.addLast(h);
    }
   
    @Override
    public Hero pull() {
        return heros.removeLast();
    }
   
    @Override
    public Hero peek() {
        return heros.getLast();
    }
       
    public static void main(String[] args) {
           
        MyStack heroStack = new MyStack();
        for (int i = 0; i < 5; i++) {
            Hero h = new Hero("hero name " + i);
            System.out.println("压入 hero:" + h);
            heroStack.push(h);
        }
        for (int i = 0; i < 5; i++) {
            Hero h =heroStack.pull();
            System.out.println("弹出 hero" + h);
        }
    }   
}

二叉树

  • 二叉树
    二叉树由各种节点组成
    二叉树特点:
    每个节点都可以有左子节点
    右子节点每一个节点都有一个值
package collection;
 
public class Node {
    // 左子节点
    public Node leftNode;
    // 右子节点
    public Node rightNode;
    // 值
    public Object value;
}
  • 二叉树排序-插入数据
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);
        }  
    }
}
  • 遍历
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());  
    }
}

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而言,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 和 HashMap 性能对比
import java.util.*;

public class Test02 {
    public static void main(String[] args) {
        HashMap<String,Hero> heroHashMap = new HashMap<>();
        List<Hero> heroList = new ArrayList<>();
        for (int i = 0; i < 3000000; i++) {
            heroList.add(new Hero("hero in list "+i));
            heroHashMap.put("hero in hashmap "+i,new Hero("hashmap hero "+i));
        }

        long startL = System.currentTimeMillis();
        for (Hero hero : heroList) {
            if (hero.name.equals("hero in list 2626435")){
                System.out.println(hero.name);
            }
            if (hero.name.equals("hero in list 126435")){
                System.out.println(hero.name);
            }
            if (hero.name.equals("hero in list 326435")){
                System.out.println(hero.name);
            }
        }
        long endL = System.currentTimeMillis();
        System.out.println("List查找时间:"+(endL-startL)+"ms");

        long startH = System.currentTimeMillis();
        System.out.println(heroHashMap.get("hero in hashmap 2626435"));
        System.out.println(heroHashMap.get("hero in hashmap 126435"));
        System.out.println(heroHashMap.get("hero in hashmap 326435"));
        long endH = System.currentTimeMillis();
        System.out.println("Hashmap查找时间:"+(endH-startH)+"ms");
    }
}

输出

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");
        System.out.println(names);
    }
}
  • 没有顺序
    Set中的元素,没有顺序。严格的说,是没有按照元素的插入顺序排列
    不保证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自身并没有独立的实现,而是在里面封装了一个Map.HashSet是作为Map的key而存在的

Collection

Collection是 Set List Queue和 Deque的接口
Queue: 先进先出队列
Deque: 双向链表
注:Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
注:Deque 继承 Queue,间接的继承了 Collection

Collections

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

  • 方法

  • 反转

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);         
    }
}

  • 混淆
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);         
    }
}

  • 排序
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);         
    }
}

  • 交换
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);         
    }
}

  • 滚动
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 把非线程安全的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); 
    }
}

HashTable

HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:HashMap可以存放 nullHashtable不能存放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); 
    }
}

泛型

集合中的泛型

  • 不使用泛型
    不使用泛型带来的问题ADHero(物理攻击英雄) APHero(魔法攻击英雄)都是Hero的子类
    ArrayList 默认接受Object类型的对象,所以所有对象都可以放进ArrayList中
    所以get(0) 返回的类型是Object
    接着,需要进行强制转换才可以得到APHero类型或者ADHero类型。
    如果软件开发人员记忆比较好,能记得哪个是哪个,还是可以的。 但是开发人员会犯错误,比如第20行,会记错,把第0个对象转换为ADHero,这样就会出现类型转换异常
package generic;
 
import java.util.ArrayList;
 
import charactor.ADHero;
import charactor.APHero;
 
public class TestGeneric {
 
    public static void main(String[] args) {
         
        ArrayList heros = new ArrayList();
         
        heros.add(new APHero());
        heros.add(new ADHero());
         
        APHero apHero =  (APHero) heros.get(0);
        ADHero adHero =  (ADHero) heros.get(1);
         
        //ADHero adHero2 =  (ADHero) heros.get(0); //出错
    }
}
  • 使用泛型
    使用泛型的好处:泛型的用法是在容器后面添加
    Type可以是类,抽象类,接口
    泛型表示这种容器,只能存放APHero,ADHero就放不进去了。
package generic;
 
import java.util.ArrayList; 
import charactor.APHero;
 
public class TestGeneric {
 
    public static void main(String[] args) {
        ArrayList<APHero> heros = new ArrayList<APHero>();
         
        //只有APHero可以放进去    
        heros.add(new APHero());
         
        //ADHero甚至放不进去
        //heros.add(new ADHero());
         
        //获取的时候也不需要进行转型,因为取出来一定是APHero
        APHero apHero =  heros.get(0);         
    }
}
  • 子类对象
    假设容器的泛型是Hero,那么Hero的子类APHero,ADHero都可以放进去
    和Hero无关的类型Item还是放不进去
package generic;
 
import java.util.ArrayList;
 
import property.Item;
 
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
 
public class TestGeneric {
 
    public static void main(String[] args) {
        ArrayList<Hero> heros = new ArrayList<Hero>();
         
        //只有作为Hero的子类可以放进去     
        heros.add(new APHero());
        heros.add(new ADHero());
         
        //和Hero无关的类型Item还是放不进去
        //heros.add(new Item());         
    }
}
  • 泛型的简写
    为了不使编译器出现警告,需要前后都使用泛型,像这样:
    ArrayList<Hero> heros = new ArrayList<Hero>();
    不过JDK7提供了一个可以略微减少代码量的泛型简写方式
    ArrayList<Hero> heros2 = new ArrayList<>();
package generic;
  
import java.util.ArrayList; 
import charactor.Hero;
  
public class TestGeneric {  
    public static void main(String[] args) {
        ArrayList<Hero> heros = new ArrayList<Hero>();
        //后面可以只用<>
        ArrayList<Hero> heros2 = new ArrayList<>();         
    }
}

支持泛型的类

  • 不支持泛型的Stack
    以Stack栈为例子,如果不使用泛型
    当需要一个只能放Hero的栈的时候,就需要设计一个HeroStack
    当需要一个只能放Item的栈的时候,就需要一个ItemStack
package generic;
   
import java.util.LinkedList; 
import charactor.Hero;
   
public class HeroStack {
   
    LinkedList<Hero> heros = new LinkedList<Hero>();
       
    public void push(Hero h) {
        heros.addLast(h);
    }
   
    public Hero pull() {
        return heros.removeLast();
    }
   
    public Hero peek() {
        return heros.getLast();
    }
       
    public static void main(String[] args) {
           
        HeroStack heroStack = new HeroStack();
        for (int i = 0; i < 5; i++) {
            Hero h = new Hero("hero name " + i);
            System.out.println("压入 hero:" + h);
            heroStack.push(h);
        }
        for (int i = 0; i < 5; i++) {
            Hero h =heroStack.pull();
            System.out.println("弹出 hero" + h);
        }
    }   
}
package generic;
   
import java.util.LinkedList;
 
import property.Item;
   
public class ItemStack {
   
    LinkedList<Item> Items = new LinkedList<Item>();
       
    public void push(Item h) {
        Items.addLast(h);
    }
   
    public Item pull() {
        return Items.removeLast();
    }
   
    public Item peek() {
        return Items.getLast();
    }
       
    public static void main(String[] args) {
           
        ItemStack ItemStack = new ItemStack();
        for (int i = 0; i < 5; i++) {
            Item item = new Item("Item name " + i);
            System.out.println("压入 Item:" + item);
            ItemStack.push(item);
        }
        for (int i = 0; i < 5; i++) {
            Item item =ItemStack.pull();
            System.out.println("弹出 Item" + item);
        }
    }   
}
  • 支持泛型的Stack
    设计一个支持泛型的栈MyStack
    设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。
    T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。
package generic;
   
import java.util.HashMap;
import java.util.LinkedList;
 
import charactor.Hero;
import property.Item;
   
public class MyStack<T> {
   
    LinkedList<T> values = new LinkedList<T>();
       
    public void push(T t) {
        values.addLast(t);
    }
   
    public T pull() {
        return values.removeLast();
    }
   
    public T peek() {
        return values.getLast();
    }
       
    public static void main(String[] args) {
        //在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
        MyStack<Hero> heroStack = new MyStack<>();
        heroStack.push(new Hero());
        //不能放Item
        heroStack.push(new Item());
         
        //在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
        MyStack<Item> itemStack = new MyStack<>();
        itemStack.push(new Item());
        //不能放Hero
        itemStack.push(new Hero());
    }   
}
  • 支持泛型的二叉树
package collection;
 
import java.util.ArrayList;
import java.util.List;
 
public class Node<T> {
    // 左子节点
    public Node<T> leftNode;
    // 右子节点
    public Node<T> rightNode;
  
    // 值
    public T value;
  
    // 插入 数据
    public void add(T t) {
        // 如果当前节点没有值,就把数据放在当前节点上
        if (null == value)
            value = t;
  
        // 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
        else {
            // 新增的值,比当前值小或者相同
             
            if ((Integer) t -((Integer)value) <= 0) {
                if (null == leftNode)
                    leftNode = new Node<T>();
                leftNode.add(t);
            }
            // 新增的值,比当前值大
            else {
                if (null == rightNode)
                    rightNode = new Node<T>();
                rightNode.add(t);
            }
  
        }
  
    }
  
 // 中序遍历所有的节点
    public List<T> values() {
        List<T> 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<Integer> roots = new Node<>();
        for (int number : randoms) {
            roots.add(number);
        }
  
        System.out.println(roots.values());  
    }
}

通配符

  • ? extends
    ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型
    heroList 的泛型可能是
    HeroheroList 的泛型可能是APHero
    heroList 的泛型可能是ADHero
    所以 可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
    但是,不能往里面放东西,因为
    放APHero就不满足
    放ADHero又不满足
package generic;
   
import java.util.ArrayList;  
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
   
public class TestGeneric {
   
    public static void main(String[] args) {
          
        ArrayList<APHero> apHeroList = new ArrayList<APHero>();
        apHeroList.add(new APHero());
         
        ArrayList<? extends Hero> heroList = apHeroList;
          
        //? extends Hero 表示这是一个Hero泛型的子类泛型
          
        //heroList 的泛型可以是Hero
        //heroList 的泛型可以使APHero
        //heroList 的泛型可以使ADHero
          
        //可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
          
        Hero h= heroList.get(0);
          
        //但是,不能往里面放东西
        //heroList.add(new ADHero()); //编译错误,因为heroList的泛型 有可能是APHero
    }      
}
  • ? super
    ArrayList heroList<? super Hero> 表示这是一个Hero泛型或者其父类泛型
    heroList的泛型可能是Hero
    heroList的泛型可能是Object
    可以往里面插入Hero以及Hero的子类
    但是取出来有风险,因为不确定取出来是Hero还是Object
package generic;
  
import java.util.ArrayList;
  
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
  
public class TestGeneric {
    public static void main(String[] args) {
  
        ArrayList<? super Hero> heroList = new ArrayList<Object>();
          
        //? super Hero 表示 heroList的泛型是Hero或者其父类泛型
          
        //heroList 的泛型可以是Hero
        //heroList 的泛型可以是Object
          
        //所以就可以插入Hero
        heroList.add(new Hero());
        //也可以插入Hero的子类
        heroList.add(new APHero());
        heroList.add(new ADHero());
          
        //但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败
        //Hero h= heroList.get(0); //报错
    }  
}
  • 泛型通配符?
    泛型通配符? 代表任意泛型
    既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能
    所以只能以 Object 的形式取出来
    并且不能往里面放对象,因为不知道到底是一个什么泛型的容器
package generic;
  
import java.util.ArrayList;
 
import property.Item;
import charactor.APHero;
import charactor.Hero;
  
public class TestGeneric {
  
    public static void main(String[] args) {
  
        ArrayList<APHero> apHeroList = new ArrayList<APHero>();
         
        //?泛型通配符,表示任意泛型
        ArrayList<?> generalList = apHeroList;
 
        //?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
        //所以只能以Object的形式取出来
        Object o = generalList.get(0);
 
        //?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
        //所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
        //generalList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item
        //generalList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero
        //generalList.add(new APHero()); //编译错误  因为?代表任意泛型,很有可能不是APHero
    }
}
  • 总结
    如果希望只取出,不插入,就使用? extends Hero
    如果希望只插入,不取出,就使用? super Hero
    如果希望,又能插入,又能取出,就不要用通配符?

泛型转型

  • 对象转型
    子类转父类
package generic;
 
import charactor.ADHero;
import charactor.Hero;
 
public class TestGeneric { 
    public static void main(String[] args) {
 
        Hero h = new Hero();
        ADHero ad = new ADHero();
        //子类转父类
        h = ad;
    } 
}
  • 子类泛型转父类泛型
    既然 子类对象 转 父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?
    如代码
    hs的泛型是父类Hero
    adhs 的泛型是子类ADHero
package generic;
 
import java.util.ArrayList;
 
import charactor.ADHero;
import charactor.Hero;
 
public class TestGeneric {
 
    public static void main(String[] args) {
        ArrayList<Hero> hs =new ArrayList<>();
        ArrayList<ADHero> adhs =new ArrayList<>();
 
        //子类泛型转父类泛型
        hs = adhs;
    } 
}
  • 父类泛型不能转子类泛型
package generic;
  
import java.util.ArrayList;  
import charactor.ADHero;
import charactor.Hero;
  
public class TestGeneric {
  
    public static void main(String[] args) {
        ArrayList<Hero> hs =new ArrayList<>();
        ArrayList<ADHero> adhs =new ArrayList<>();
  
        //假设能成功
        adhs = hs;
         
        //这个时候adhs实际上指向的是泛型是Hero的容器,而这个容器里可能放的是一个APHero
        //而根据泛型,直接取出来就转型成了ADHero
        //所以就变成了APHero转型成ADHero,这是矛盾的。
        ADHero ad =adhs.get(0);        
    }  
}
posted @ 2020-07-09 14:15  鹏懿如斯  阅读(189)  评论(0编辑  收藏  举报