集合框架
先看个思维导图呗
一.结构图
二.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
排序的第一个步骤是把数据插入到该二叉树中
插入基本逻辑是,小、相同的放左边,大的放右边
- 67 放在根节点
- 7 比 67小,放在67的左节点
- 30 比67 小,找到67的左节点7,30比7大,就放在7的右节点
- 73 比67大, 放在67的右节点
- 10 比 67小,找到67的左节点7,10比7大,找到7的右节点30,10比30小,放在30的左节点。
…
… - 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的所有对象找出来
要求使用两种办法来寻找
- 不使用HashMap,直接使用for循环找出来,并统计花费的时间
- 借助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次,才能找到对应的英雄。
测试逻辑:
- 初始化2000000个对象到ArrayList中
- 打乱容器中的数据顺序
- 进行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 做同样的查找
- 初始化2000000个对象到HashMap中。
- 进行10次查询
- 统计每一次的查询消耗的时间
可以观察到,几乎不花时间,花费的时间在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的所有对象找出来
要求使用两种办法来寻找
- 不使用MyHashMap,直接使用for循环找出来,并统计花费的时间
- 借助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表达式。