使用数组的局限性
如果要存放多个对象,可以使用数组,但是数组有局限性
比如 声明长度是10的数组
不用的数组就浪费了
超过10的个数,又放不下
package collection;
public class TestCollection {
public static void main(String[] args) {
//数组的局限性
Hero heros[] = new Hero[10];
//声明长度是10的数组
//不用的数组就浪费了
//超过10的个数,又放不下
heros[0] = new Hero("盖伦");
//放不下要报错
heros[20] = new Hero("提莫");
}
}
--------------------------------
Hero.java
package collection;
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;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
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());
}
}
增加
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);
}
}
练习-判断是否相同
如果就是要判断集合里是否存在一个 name等于 "hero 1"的对象,应该怎么做?
package collection;
import java.util.ArrayList;
import charactor.Hero;
/**
* 练习-判断是否相同
*
* 如果就是要判断集合里是否存在一个 name等于 "hero 1"的对象,应该怎么做?
*/
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来进行判断(不重写方法)*/
Hero[] hs = (Hero[]) heros.toArray(new Hero[] {});
for(Hero h:hs) {
if(h.name.equals("hero 1")) {
System.out.println("找到了name是hero 1的对象");
}
}
/*方法二:Hero重写toString()方法为return name*/
for(int i = 0;i<heros.size();i++) {
if(heros.get(i).toString().equals("hero 1")) {
System.out.println("找到了name是hero 1的对象");
}
}
}
}
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 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);
}
}
--------------------------------------------------------
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("物品使用后,可以有效果");
}
}
泛型的简写
为了不使编译器出现警告,需要前后都使用泛型,像这样:
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<>();
}
}
练习-支持泛型的ArrayList
借助泛型和前面学习的面向对象的知识,设计一个ArrayList,使得这个ArrayList里,又可以放Hero,又可以放Item,但是除了这两种对象,其他的对象都不能放
package collection;
import java.util.ArrayList;
public class GenericArrayList<T> extends ArrayList {
public boolean add(Object o){
if(o instanceof Hero||o instanceof Item){
//判断是否是Hero或者是Item
super.add(o);//调用父类
return true;
}else {
return false;
}
}
public void add(int index,Object o){
if(o instanceof Hero || o instanceof Item)
super.add(index,o);
return;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
GenericArrayList<Hero> list = new GenericArrayList();
Hero hero = new Hero("火男");
Item item = new Item("冰杖");
String str = "hsjd";
//添加测试
list.add(hero);
list.add(item);
list.add(str);
System.out.println(list);
}
}
用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遍历集合中的元素

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循环也有不足:
无法用来进行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的倍数的对象
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> a = new ArrayList<>();
for (int i = 0; i < 100; i++) {
a.add("Hero" + i + " ");
}
Iterator<String> it = a.iterator();
while (it.hasNext()) {
String s = it.next();
if ((Integer.parseInt(s.substring(4, 6).trim())) % 8 == 0) {
System.out.println(s);
}
}
}
}
集合框架LinkedList
序列分先进先出FIFO,先进后出FILO
FIFO在Java中又叫Queue 队列
FILO在Java中又叫Stack 栈
LinkedList 与 List接口
与ArrayList一样,LinkedList也实现了List接口,诸如add,remove,contains等等方法。
双向链表 - 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.Arrays;
import java.util.LinkedList;
/**
* 与FIFO(先入先出的)队列类似的一种数据结构是FILO先入后出栈Stack
根据接口Stack :
实现类:MyStack
public class MyStack implements Stack
并向这个栈中,压入5个英雄,接着弹出5个英雄
再解释一下栈:
栈的结构,就像给弹夹添加子弹一样,先添加的子弹,就放在了最下面,打手枪的时候,只能从最上面取子弹。
*/
public class MyStack implements Stack {
LinkedList<Hero> heros = new LinkedList();//提供双链表方法
public static void main(String[] args) {
MyStack ms = new MyStack();
for (int i = 1; i <= 5; i++) {
Hero h = new Hero("hero"+i);
System.out.println("压入英雄:"+h);
ms.push(h); //压入
}
System.out.println("-------------------");
for (int i = 1; i <= 5; i++) {
Hero h = ms.pull(); //弹出
System.out.println("弹出英雄:"+h);
}
}
@Override
public void push(Hero h) {
heros.addLast(h); //基于双向链表实现方法(加入最后)
}
@Override
public Hero pull() {
return heros.removeLast(); //先入后出(删除最后一位)
}
@Override
public Hero peek() {
return heros.getLast(); //查看最后一位
}
}
二叉树插入,排序,遍历
二叉树由各种节点组成
二叉树特点:
每个节点都可以有左子节点,右子节点
每一个节点都有一个值
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) {
// TODO Auto-generated method stub
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());
}
}
练习-比较冒泡法,选择法以及二叉树排序的性能区别
创建4万个随机数,然后用分别用冒泡法,选择法,二叉树3种排序算法进行排序,比较哪种更快
public class Hw07 {
public static void main(String[] args) {
//生成四万个随机数存储在数组中
int[] numbers=new int[40000];
for(int i=0;i<numbers.length;i++){
numbers[i]=(int)(1000*Math.random());
}
long a1=System.currentTimeMillis();
//冒泡排序
bubbleSort(numbers);
System.out.println();
long a2=System.currentTimeMillis();
System.out.println("冒泡排序所用的时间为:"+(a2-a1)+"ms.");
for(int i=0;i<numbers.length;i++){
numbers[i]=(int)(1000*Math.random());
}
long b1=System.currentTimeMillis();
//选择排序
selectSort(numbers);
long b2=System.currentTimeMillis();
System.out.println();
System.out.println("选择排序所用的时间为:"+(b2-b1)+"ms.");
for(int i=0;i<numbers.length;i++){
numbers[i]=(int)(1000*Math.random());
}
long c1=System.currentTimeMillis();
//二叉树排序
//构建一个二叉树
BNode root=new BNode();
for(int value:numbers){
creatBtree(root,value);
}
//二叉树排序
btreeSort(root);
long c2=System.currentTimeMillis();
System.out.println();
System.out.println("二叉树排序的时间为:"+(c2-c1)+"ms.");
}
//根据根结点创建一个二叉树
private static void creatBtree(BNode root,int value){
//如果根结点中没有值则直接将值添加到根结点
if(root!=null){
if(root.value==null){
root.value=value;
}else if(value-(int)root.value<=0){
//添加到左结点中
if(root.leftNode==null){
root.leftNode=new BNode();
}
creatBtree(root.leftNode,value);
}else{
//添加到右结点中
if(root.rightNode==null){
root.rightNode=new BNode();
}
creatBtree(root.rightNode,value);
}
}
}
//遍历二叉树,相当于排序
private static void btreeSort(BNode root){
if(root!=null){
btreeSort(root.leftNode);
System.out.print(root.value+"->");
btreeSort(root.rightNode);
}
}
//冒泡排序
private static void bubbleSort(int[] numbers){
int[] newNums=new int[40000];
newNums=numbers;
int length=newNums.length;
for(int i=0;i<length-1;i++){
for(int j=0;j<length-i-1;j++){
if(newNums[j]>newNums[j+1]){
int temp=newNums[j];
newNums[j]=newNums[j+1];
newNums[j+1]=temp;
}
}
}
for(int number:newNums){
System.out.print(number+"->");
}
}
//选择排序
private static void selectSort(int[] numbers){
int len=numbers.length;
for(int i=0;i<len-1;i++){
int min=i;//记录下标有利于交换元素
for(int j=i+1;j<len;j++){
if(numbers[j]<numbers[min]){
min=j;
}
}
int temp=numbers[min];
numbers[min]=numbers[i];
numbers[i]=temp;
}
for(int num:numbers){
System.out.print(num+"->");
}
}
}
//二叉树结点
class BNode{
Integer value;
BNode leftNode;
BNode rightNode;
}
------------------------------------------------------------------
package collection;
import java.util.Arrays;
import java.util.List;
/**
* 练习-比较冒泡法,选择法以及二叉树排序的性能区别
*
* 创建4万个随机数,然后用分别用冒泡法,选择法,二叉树3种排序算法进行排序,比较哪种更快
*/
public class SortCompare {
public static void main(String[] args) {
int total = 40 * 1000;
System.out.println("初始化一个长度是" + total + "的随机数字的数组");
System.out.println("......");
int[] nums = new int[total];
for (int i = 0; i < total; i++) {
nums[i] = (int) (Math.random() * total);
}
System.out.println("初始化完毕");
System.out.println("开始冒泡排序");
long start = System.currentTimeMillis();
int[] sortedByBubbling = BubblingSort(nums);
long end = System.currentTimeMillis();
System.out.printf("排序完毕,所用时间为%d毫秒%n", end - start);
System.out.println("开始选择排序");
start = System.currentTimeMillis();
int[] sortedBySelection = SelectionSort(nums);
end = System.currentTimeMillis();
System.out.printf("排序完毕,所用时间为%d毫秒%n", end - start);
System.out.println("开始二叉树排序");
start = System.currentTimeMillis();
Node roots = new Node();
for (int number : nums) {
roots.add(number);
}
end = System.currentTimeMillis();
System.out.printf("排序完毕,所用时间为%d毫秒%n", end - start);
// System.out.println("比较 选择法 和 冒泡法 排序结果:");
// System.out.println(Arrays.equals(sortedByBubbling, sortedBySelection));
// System.out.println("比较 选择法 和 二叉树 排序结果:");
// List<Object> list = roots.values(); // 当total过大时,会出现栈溢出异常
// int[] sortedByBinaryTree = new int[list.size()];
// for (int i = 0; i < sortedByBinaryTree.length; i++) {
// sortedByBinaryTree[i] = Integer.parseInt(list.get(i).toString());
// }
// System.out.println(Arrays.equals(sortedBySelection, sortedByBinaryTree));
}
// 冒泡排序
public static int[] BubblingSort(int[] nums) {
for (int i = 1; i < nums.length; i++) // 趟数
for (int j = 0; j < nums.length - i; j++) // 每趟的比较次数
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
return nums;
}
// 选择排序
public static int[] SelectionSort(int[] nums) {
for (int i = 0; i < nums.length; i++) // 趟数
for (int j = i + 1; j < nums.length; j++)
if (nums[j] < nums[i]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
return nums;
}
}
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其中存放3000000(三百万个)Hero对象,其名称是随机的,格式是hero-[4位随机数]
hero-3229
hero-6232
hero-9365
...
因为总数很大,所以几乎每种都有重复,把名字叫做 hero-5555的所有对象找出来
要求使用两种办法来寻找
1. 不使用HashMap,直接使用for循环找出来,并统计花费的时间
2. 借助HashMap,找出结果,并统计花费的时间
package collection;
import stream.Hero;
import java.util.ArrayList;
import java.util.HashMap;
public class z123 {
public static void main(String[] args) {
ArrayList<Hero> alst = new ArrayList<>();
HashMap<String,ArrayList<Hero>> hMap = new HashMap<>();
for(int i=0;i<3000000;i++){
Hero h = new Hero("hero-"+(int)((Math.random()*9+1)*1000));
alst.add(h);
}
Long start = System.currentTimeMillis();
ArrayList<Hero> result = new ArrayList<Hero>();
for(Hero h:alst){
String sName = h.name;
if(sName.equals("hero-5555")){
result.add(h);
}
}
Long end = System.currentTimeMillis();
System.out.printf("通过for查找,"+ "一共找到%d重复个英雄,耗时%d毫秒%n",result.size(),end-start);
Long start1 = System.currentTimeMillis();
for(Hero h:alst){
ArrayList<Hero> hero = new ArrayList<>();
String sname = h.name;
if(hMap.containsKey(sname)){
hero = hMap.get(sname);
hero.add(h);
}else{
hero.add(h);
hMap.put(sname,hero);
}
}
ArrayList<Hero> ho = hMap.get("hero-5555");
Long end1 = System.currentTimeMillis();
System.out.printf("通过map查找,"+ "一共找到%d重复个英雄,耗时%d毫秒%n",ho.size(),end1-start1);
}
}
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中的元素,没有顺序。
严格的说,是没有按照元素的插入顺序排列
HashSet的具体顺序,既不是按照插入顺序,也不是按照hashcode的顺序。
换句话说,同样是插入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对象,因为是一个类属性,所以只会有一个。
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();
}
}
Collection是一个接口
Collection
Collection是 Set List Queue和 Deque的接口
Queue: 先进先出队列
Deque: 双向链表
注:Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
注:Deque 继承 Queue,间接的继承了 Collection

Collections是一个类,容器的工具类,就如同Arrays是数组的工具类
反转
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 混淆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 对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 交换两个数据的位置

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 把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 把非线程安全的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.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 练习-统计概率
*
* 首先初始化一个List,长度是10,值是0-9。
然后不断的shuffle,直到前3位出现
3 1 4
shuffle 1000,000 次,统计出现的概率
*/
public class TestCollections {
public static void main(String[] args) {
// 初始化
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
numbers.add(i);
}
System.out.println("集合中的数据:");
System.out.println(numbers);
int total = 1000 * 1000; // 混淆的次数
int sum = 0; // 统计前3位为 3 1 4 的次数
for (int i = 0; i < total; i++) {
Collections.shuffle(numbers);
if (numbers.get(0) == 3 && numbers.get(1) == 1 && numbers.get(2) == 4) {
sum++;
}
}
System.out.printf("shuffle 1000,000 次,出现的次数为%d,", sum);
System.out.println("概率为" + (double) sum / total * 100 + "%");
}
}
集合框架的关系与区别
是否有顺序
ArrayList: 有顺序
HashSet: 无顺序
HashSet的具体顺序,既不是按照插入顺序,也不是按照hashcode的顺序。关于hashcode有专门的章节讲解: hashcode 原理。
以下是HasetSet源代码中的部分注释
/**
* 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.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相同,则是相同数据,否则是不同数据
更多关系hashcode,请参考hashcode原理

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 random;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Set<Integer> s = new HashSet<>();
while (true){
s.add((int)(Math.random()*9999));
if (s.size()==50){
break;
}
}
System.out.println(s.size());
System.out.println(s);
}
}
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);
}
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);
}
long end = System.currentTimeMillis();
System.out.printf("%s总长度是%d,定位到第%d个数据,取出来,加1,再放回去%n 重复%d遍,总共耗时 %d 毫秒 %n", type,total, index,total, end - start);
System.out.println();
}
}
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);
}
}
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);
System.out.println(numberSet1);
LinkedHashSet<Integer> numberSet2 =new LinkedHashSet<Integer>();
//LinkedHashSet中的数据是按照插入顺序存放
numberSet2.add(88);
numberSet2.add(8);
numberSet2.add(888);
System.out.println(numberSet2);
TreeSet<Integer> numberSet3 =new TreeSet<Integer>();
//TreeSet 中的数据是进行了排序的
numberSet3.add(88);
numberSet3.add(8);
numberSet3.add(888);
System.out.println(numberSet3);
}
}
本文深入探讨了JAVA集合框架,包括ArrayList和LinkedList的特点及操作,如增加、删除、遍历等。此外,还介绍了泛型、HashSet、HashMap及其区别,以及集合框架中的List接口、Deque接口和Queue接口的使用。通过实例展示了如何使用这些容器,同时提供了多种遍历和操作集合的方法,以及对比了不同数据结构的性能和适用场景。

791

被折叠的 条评论
为什么被折叠?



