【JavaSE 七】集合框架

七、集合框架

ArrayList

和数组的区别

import java.util.ArrayList;
public class test {
    public static void main(String[] args) {
        Person p1 = new Person("飞机");
        Person p2 = new Person("火车");
        //利用ArrayList,容量可变
        ArrayList al = new ArrayList();
        al.add(p1);
        al.add(p2);
        //输出和数组不同是因为,集合对象在toString()过程中会调用AbstractCollection的toString()塑形
        //需要显示对象信息,就得重写toString()方法
        System.out.println(al);

        //利用数组,长度固定
        Person[] pArray = new Person[2];
        pArray[0] = p1;
        pArray[1] = p2;
        System.out.println(pArray);
    }
}

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

常用方法

import java.util.ArrayList;
public class test {
    public static void main(String[] args) {
        ArrayList al = new ArrayList();
    //一、add()对象直接加在集合最后面
        for (int i = 0; i < 5; i++) {
            al.add(new Person("飞机"+i));
        }
        System.out.println(al);
        //指定位置添加对象,指定位置不能超过集合现有容量
        Person p1 = new Person("火车");
        al.add(2,p1);
        System.out.println(al);
    //二、contains()判断一个对象是否在容器中,必须是同一个对象
        System.out.println(al.contains(p1));
    //三、get()获取指定位置的对象,如果输入的下标越界,会报错
        System.out.println(al.get(3));
    //四、indexof()获取对象所处的位置索引,如果没有该对象,则返回-1
        System.out.println(al.indexOf(p1));
    //五、remove()删除集合中的对象,参数可以是索引也可以是对象
        al.remove(p1);
        System.out.println(al);
    //六、set()用于替换指定位置的元素为指定对象
        al.set(2,p1);
        System.out.println(al);
    //七、size()获取集合的大小
        System.out.println(al.size());
    //八、toArray()将集合转化为数组,不指定类型转换的数组是Object类型
        System.out.println(al.toArray());
        //指定Person数组
        Person[] pA = new Person[]{};
        System.out.println(al.toArray(pA));
    //九、addAll()将一个集合加入另一个集合中
        ArrayList al2 = new ArrayList();
        al2.add(new Person("汽车"));
        al2.add(new Person("自行车"));
        al.addAll(al2);
        System.out.println(al);
        al.addAll(1,al2);//指定位置添加
        System.out.println(al);
        al.addAll(al);//居然可以把自己添加到自己
        System.out.println(al);
    //十、clear()清空一个集合
        al.clear();
        System.out.println(al);
    }
}

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
    public void say(){
        System.out.println("ha");
    }
}

List接口

​ 使用的时候用List来引用ArrayList对象,在后期需要对集合进行更改时,改动只需一行代码

​ List list = new ArrayList()

泛型初识

​ 不指定泛型的容器,可以存放任何类型的元素

​ 指定了泛型的容器,只能存放指定类型的元素以及其子类,取出不需要转型

遍历

import java.util.ArrayList;
import java.util.Iterator;

public class test {
    public static void main(String[] args) {
        ArrayList<Person> al = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            al.add(new Person("飞机"+i));
        }
    //第一种遍历for循环
        for (int i = 0; i < al.size(); i++) {
            System.out.println(al.get(i));
        }
    //迭代器遍历Iterator
        Iterator<Person> it = al.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    //增强for循环遍历
        for (Person p : al) {
            System.out.println(p);
        }
    }
}

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
    public void say(){
        System.out.println("ha");
    }
}

LinkedList

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

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

import java.util.LinkedList;

public class test {
    public static void main(String[] args) {
        LinkedList<Person> al = new LinkedList<>();
        al.add(new Person("苹果"));
        al.add(new Person("西瓜"));
        al.add(new Person("葡萄"));
        System.out.println(al);
        //可以很方便的在前后插入元素
        al.addFirst(new Person("第一个"));
        al.addLast(new Person("后一个"));
        System.out.println(al);
        //可以方便查看最前与最后的元素
        System.out.println(al.getFirst());
        System.out.println(al.getLast());
        //可以方便的移除最前最后元素
        al.removeFirst();//返回取出的值
        al.removeLast();//返回取出的值
        System.out.println(al);
    }
}
class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
    public void say(){
        System.out.println("ha");
    }
}

​ LinkedList 除了实现了List和Deque外,还实现了Queue接口(队列)

import java.util.LinkedList;

public class test {
    public static void main(String[] args) {
        LinkedList<Person> al = new LinkedList<>();
        al.add(new Person("苹果"));
        al.add(new Person("西瓜"));
        al.add(new Person("葡萄"));
        System.out.println(al);
        //加在队列的最后面
        al.offer(new Person("offer()"));
        System.out.println(al);
        //查看一个元素
        System.out.println(al.peek());
        System.out.println(al.peekFirst());
        System.out.println(al.peekLast());
        System.out.println(al);
        //取出一个元素
        al.poll();//返回取出的值
        al.pollFirst();//返回取出的值
        al.pollLast();//返回取出的值
        System.out.println(al);
    }
}
class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
    public void say(){
        System.out.println("ha");
    }
}

二叉树

二叉树概念

​ 二叉树由各种节点组成,二叉树特点:每个节点都可以有左子节点,右子节点 ,每一个节点都有一个

二叉树排序-插入数据和排序

import java.util.ArrayList;
import java.util.List;

public class test {
    public static void main(String[] args) {
        int[] numb = {67, 7, 30, 73, 10, 0, 78, 81, 10, 74};
        Node roots = new Node();
        for (int in : numb) {
            roots.add(in);
        }
        System.out.println(roots.inorder());
    }

}
class Node{
    public Node leftNode;
    public Node rightNode;
    public Object value;

    public void add(int v){
        if(null==value){
            value = v;
        }else{
            if((Integer)v<=(Integer) value) {
                if(leftNode == null){
                    leftNode =  new Node();
                    leftNode.value = v;
                }else {
                    leftNode.add(v);
                }

            }else {
                if(rightNode == null){
                    rightNode =  new Node();
                    rightNode.value = v;
                }else {
                    rightNode.add(v);
                }
            }
        }
    }
    public List inorder(){
        List list = new ArrayList();
        if(leftNode!=null){
            list.addAll(leftNode.inorder());
        }
//        System.out.print(value+"  ");
        list.add(value);
        if(rightNode!=null){
            list.addAll(rightNode.inorder());
        }
        return list;
    }
}

HashMap

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

​ 对于HashMap而言,key是唯一的,不可以重复的

​ 所以,以相同的key 把不同的value插入到 Map中会导致旧元素被覆盖,只留下最后插入的元素。

import java.util.HashMap;

public class test {
    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<String, Integer>();
        hm.put("一",12);
        hm.put("二",15);
        hm.put("三",27);
        System.out.println(hm);//从打印结果来看,HashMap插入是无序的
        System.out.println(hm.get("三"));
    }
}

HashSet

​ Set中的元素,不能重复,Set中的元素,没有顺序,严格的说,是没有按照元素的插入顺序排列

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

​ Set不提供get()来获取指定位置的元素

import java.util.HashSet;
import java.util.Iterator;

public class test {
    public static void main(String[] args) {
        HashSet<Integer> hs = new HashSet<>();
        hs.add(3);
        hs.add(5);
        hs.add(8);
        hs.add(9);
        System.out.println(hs);
        //没有get()方法。只能使用迭代器和增强for循环来遍历
        //使用迭代器
        Iterator it = hs.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        //for循环
        for (Integer i : hs) {
            System.out.println(i);
        }

    }
}

Collection

​ Collection是 Set、List、Queue(先进先出队列)和 Deque(双向链表)的接口

​ Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的

Collections

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println("原集合:"+list);
        Collections.reverse(list);//反转集合中的元素
        System.out.println("反转后:"+list);
        Collections.shuffle(list);//打乱集合中的元素
        System.out.println("打乱后:"+list);
        Collections.sort(list);//对集合中的元素进行排序
        System.out.println("排序后:"+list);
        Collections.swap(list,4,6);//交换两个元素
        System.out.println("交换后:"+list);
        Collections.rotate(list,3);//把List中的数据,向右滚动指定单位的长度
        System.out.println("滚动后:"+list);
        Collections.synchronizedList(list);//把非线程安全的List转换为线程安全的List
    }
}

ArrayList和HashSet的区别

​ ArrayList: 有顺序,HashSet: 无顺序

​ List中的数据可以重复,Set中的数据不能够重复

​ 重复判断标准是:hashcode相同且equals相同

ArrayList和Linkedlist的区别

​ ArrayList 插入,删除数据慢,LinkedList插入,删除数据快

​ ArrayList是顺序结构,所以定位很快,指哪找哪。 就像电影院位置一样,有了电影票,一下就找到位置了

​ LinkedList 是链表结构,就像手里的一串佛珠,要找出第99个佛珠,必须得一个一个的数过去,所以定位慢

HashMap和HashTable的区别

​ HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式

​ 区别1:

​ HashMap可以存放 null

​ Hashtable不能存放null

​ 区别2:

​ HashMap不是线程安全的类

​ Hashtable是线程安全的类

其他几种Set

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

Hashcode原理

​ 所有对象都对应一个Hashcode,Hashcode的值有相同的可能,

​ 保存的时候不同的Hashcode放在指定的位置,相同的以链表的形式放在同一个位置

​ 查找是依据指定位置查找,如果指定位置有多个就逐一比较(减少了比较次数)

​ 这是一种用空间换时间的思维方式

比较器

当需要以对象中的属性大小来排列集合元素时,需要使用比较器,有两种方式来使用

第一种时使用Comparator

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class txt {

    public static void main(String[] args){
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三",15,1500));
        list.add(new Person("王五",62,6500));
        list.add(new Person("李四",30,900));
        list.add(new Person("王八",2,7543));
        list.add(new Person("刘七",56,1500));
        System.out.println(list);
        list.sort(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                if(o1.salary <= o2.salary){
                    return 0;//0和1都可以
                }else {
                    return -1;
                }
            }
        });
        System.out.println(list);
    }
}

class Person {
    public String name;
    public int age;
    public int salary;

    public Person(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

第二种是让对象实现Comparable接口,然后重写compareTo方法定义比较方式

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class txt {

    public static void main(String[] args){
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三",15,1500));
        list.add(new Person("王五",62,6500));
        list.add(new Person("李四",30,900));
        list.add(new Person("王八",2,7543));
        list.add(new Person("刘七",56,3452));
        System.out.println(list);
        Collections.sort(list);
        System.out.println(list);
    }
}

class Person implements Comparable<Person>{
    public String name;
    public int age;
    public int salary;

    public Person(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
    @Override
    public int compareTo(Person p) {
        if(this.age < p.age){
            return 1;
        }else{
            return -1;
        }

    }
}


聚合操作

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

​ 但是要用好聚合,必须先掌握Lambda表达式,聚合的章节讲解放在Lambda与聚合操作部分详细讲解

练习

1)集合里查找名称等于指定字符串的对象

import java.util.ArrayList;
import java.util.List;

public class Exercise {
    public static void main(String[] args){
        List<Person> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(new Person("名称"+i));
        }
        System.out.println(list);
        System.out.println(isContain(list,"名称3"));
    }
    public static boolean isContain(List<Person> list,String name){
        for (Person p : list) {
            if(p.name.equals(name)){
                return true;
            }
        }
        return false;
    }

}
class Person{
    public String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

2)MyStringBuffer练习

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyStringBuffer implements InterfaceStringBuffer {
    private List list;
    public MyStringBuffer() {
        list = new ArrayList();
    }

    public MyStringBuffer(String str) throws Exception {
        list = new ArrayList();
        append(str);
    }

    @Override
    public void append(String str) throws Exception {
        insert(list.size(),str);
    }

    @Override
    public void append(char c) throws Exception {
        insert(list.size(),c);
    }

    @Override
    public void insert(int pos, char b) throws Exception {
        if(pos<=list.size()){
            list.add(pos,b);
        }

    }

    @Override
    public void insert(int pos, String str) throws Exception {
        char[] ch = str.toCharArray();
        for (int i = 0; i < ch.length; i++) {
            insert(pos+i,ch[i]);
        }
    }


    @Override
    public void delete(int start) throws Exception {
        if(start<list.size()){
            list.remove(start);
        }

    }

    @Override
    public void delete(int start, int end) throws Exception {
        if(end>start){
            for (int i = 0; i < end-start+1; i++) {
                delete(start);
            }
        }
    }

    @Override
    public void reverse() {
//        //相当与列表反转
        Collections.reverse(list);
    }

    @Override
    public int length() {
        return list.size();
    }

    @Override
    public String toString() {
        String str="";
        for (Object o : list) {
            str = str + o;
        }
        return str;
    }
    public int capacity(){
        return 0;
    }
}

3)删除ArrayList中的数据

​ 首先初始化一个Person集合,里面放100个Person对象,名称分别是从0-99,通过遍历的手段,删除掉名字编号是8的倍数的对象

import java.util.ArrayList;
import java.util.List;

public class Exercise {
    public static void main(String[] args){
        List<Person> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(new Person("名称 "+i));
        }
        System.out.println(list);//初始列表
        List<Person> l = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            int index =Integer.parseInt(list.get(i).name.substring(3));
            if(index%8 == 0){
                l.add(list.get(i));
            }
        }
        System.out.println(l);//可以被8整除的列表
        list.removeAll(l);
        System.out.println(list);//去除被8整除的列表
    }

}

4)使用LinkedList实现Stack栈

​ 与FIFO(先入先出的)队列类似的一种数据结构是FILO先入后出Stack

​ 根据接口Stack ,实现类:MyStack,public class MyStack implements Stack

​ 并向这个栈中,压入5个Person,接着弹出5个Person

Stack.java

public interface Stack {

    //推入到最后位置
    public void push(Person h);
    //把最后一个取出来
    public Person pull();
    //查看最后一个
    public Person peek();
}

MyStack.java

import java.util.LinkedList;

public class MyStack implements Stack{
    public LinkedList<Person> llist;

    public MyStack() {
        llist = new LinkedList<>();
    }
    @Override
    public void push(Person h) {
        llist.push(h);
    }

    @Override
    public Person pull() {
        return llist.poll();
    }

    @Override
    public Person peek() {
        return llist.peek();
    }
}

Exercise.java

public class Exercise {
    public static void main(String[] args){
        MyStack ms = new MyStack();
        ms.push(new Person("阿一"));
        ms.push(new Person("阿二"));
        ms.push(new Person("阿三"));
        ms.push(new Person("阿四"));
        ms.push(new Person("阿五"));
        System.out.println(ms.peek());
        ms.pull();
        System.out.println(ms.peek());
        ms.pull();
        System.out.println(ms.peek());
        ms.pull();
        System.out.println(ms.peek());
        ms.pull();
        System.out.println(ms.peek());
    }
}

5)Person的二叉树练习

​ 设计一个person二叉树,PersonNode,可以向这个二叉树里插入不同的Person对象,并按照Person的年龄排序

​ 随机生成10个Person对象,每个Person对象都有不同的年龄,插入这个PersonNode后,把排序结果打印出来。

Exercise.java

import java.util.Random;

public class Exercise {
    public static void main(String[] args){
        PersonNode pn = new PersonNode();
        Random rd = new Random();
        for (int i = 0; i < 10; i++) {
            pn.add(new Person("张三", rd.nextInt(120)));
        }
        inorder(pn);
    }

    public static void inorder(PersonNode p) {
        if(p!=null){
            if(p.getLeftNode() != null){
                inorder(p.getLeftNode());
            }
            System.out.print(p.getP()+" ");

            if(p.getRightNode() != null){
                inorder(p.getRightNode());
            }
        }
    }
}

PersonNode.java

public class PersonNode {
   private PersonNode leftNode;
   private PersonNode rightNode;
   private Person p;

    public void add(Person pe ){
        if(this.p == null){
           this.p = pe;
        }else {
            if(this.p.age > pe.age){
                if(this.leftNode == null){
                    this.leftNode = new PersonNode();
                }
                this.leftNode.add(pe);
            }else {
                if(this.rightNode == null){
                    this.rightNode = new PersonNode();
                }
                this.rightNode.add(pe);
            }
        }
    }

    public PersonNode getLeftNode() {
        return leftNode;
    }

    public PersonNode getRightNode() {
        return rightNode;
    }

    public Person getP() {
        return p;
    }
}

6)排序的性能区别

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

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

冒泡排序

import java.util.ArrayList;
import java.util.Random;
/***
 * 二叉花费时间:47ms
 * 选择花费时间:4312ms
 * 冒泡花费时间:9718ms
 */
public class Exercise {
    public static void main(String[] args){
        //准备40000个随机数据
        Random rd = new Random();
        ArrayList<Integer> radm = new ArrayList<>();
        for (int i = 0; i < 40000; i++) {
            radm.add(rd.nextInt(100000));
        }

        //二叉树排序
        long startTime = System.currentTimeMillis();
        NumBinaryTree nbt = new NumBinaryTree();
        for (int i = 0; i < 40000; i++) {
            nbt.add(radm.get(i));
        }
        ArrayList<Integer> radmSort = new ArrayList<>();
//        System.out.println("原数据:"+radm);
        inorder(radmSort,nbt);
//        System.out.println("二叉排序结果:"+radmSort);
        long endTime = System.currentTimeMillis();
        System.out.println("二叉花费时间:"+ (endTime - startTime) + "ms");

        //选择排序
        long startTime1 = System.currentTimeMillis();
        selectionSort(radm);
        long endTime1 = System.currentTimeMillis();
        System.out.println("选择花费时间:"+ (endTime1 - startTime1) + "ms");

        //冒泡排序
        long startTime2 = System.currentTimeMillis();
        bubbleSort(radm);
        long endTime2 = System.currentTimeMillis();
        System.out.println("冒泡花费时间:"+ (endTime2 - startTime2) + "ms");
    }
    public static void selectionSort(ArrayList<Integer> radm){
        //选择排序时,每次循环内找出最小的都调换比较费时,而找出最小后循环外再做调换,可以节省时间
        ArrayList<Integer> radmCopy = new ArrayList<>(radm);//为了不影响原列表,复制一个副本来操作
        for (int i = 0; i < radmCopy.size()-1; i++) {
            //循环外对调
            int minIndex = i;
            for (int j = i+1; j < radmCopy.size(); j++) {
                if(radmCopy.get(minIndex)>radmCopy.get(j)) {
                    minIndex = j;
                }
            }
            Integer temp = radmCopy.get(minIndex);
            radmCopy.set(minIndex,radmCopy.get(i));
            radmCopy.set(i,temp);
//            //循环内对调
//            for (int j = i+1; j < radmCopy.size(); j++) {
//                if(radmCopy.get(i)>radmCopy.get(j)) {
//                    Integer temp = radmCopy.get(i);
//                    radmCopy.set(i,radmCopy.get(j));
//                    radmCopy.set(j,temp);
//                }
//            }

        }
//        System.out.println("选择排序结果:"+radmCopy);
    }
    public static void bubbleSort(ArrayList<Integer> radm){
        ArrayList<Integer> radmCopy1 = new ArrayList<>(radm);//为了不影响原列表,复制一个副本来操作
        for (int i = 0; i < radmCopy1.size()-1; i++) {
            for (int j = 0; j < radmCopy1.size()-i-1; j++) {
                if(radmCopy1.get(j) > radmCopy1.get(j+1)){
                    Integer temp = radmCopy1.get(j);
                    radmCopy1.set(j,radmCopy1.get(j+1));
                    radmCopy1.set(j+1,temp);
                }
            }
        }

//        System.out.println("冒泡排序结果:"+radmCopy1);
    }

    public static void inorder(ArrayList l,NumBinaryTree p) {
        if(p!=null){
            if(p.getLeftNode() != null){
                inorder(l,p.getLeftNode());
            }
//            System.out.print(p.getValue()+" ");
            l.add(p.getValue());
            if(p.getRightNode() != null){
                inorder(l,p.getRightNode());
            }
        }
    }
}

NumBinaryTree.java

public class NumBinaryTree {
    private NumBinaryTree leftNode;
    private NumBinaryTree rightNode;
    private Integer value;

    public void add(Integer i){
        if(value == null){
            value = i;
        }else {
            if(i<value){
                if (leftNode == null){
                    leftNode = new NumBinaryTree();
                    leftNode.value = i;
                }else {
                   leftNode.add(i);
                }
            }else {
                if (rightNode == null){
                    rightNode = new NumBinaryTree();
                    rightNode.value = i;
                }else {
                    rightNode.add(i);
                }
            }

        }
    }
    public NumBinaryTree getLeftNode() {
        return leftNode;
    }

    public NumBinaryTree getRightNode() {
        return rightNode;
    }

    public int getValue() {
        return value;
    }
}

7)查找内容性能比较

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

​ 因为总数很大,所以几乎每种都有重复,把名字叫做 person-5555的所有对象找出来

​ 要求使用两种办法来寻找

​ 1、不使用HashMap,直接使用for循环找出来,并统计花费的时间

​ 2、借助HashMap,找出结果,并统计花费的时间

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Exercise {
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<>();
        for (int i = 0; i < 3000000;i++) {
            list.add(new Person("person-"+((int)(Math.random()*9000)+1000)));
        }
        //for循环查询
        long startTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < list.size(); i++) {
            if(list.get(i).name.equals("person-5555")){
                count++;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("找到名称为person-5555的数量:"+count);
        System.out.println("for循环总共用时:"+(endTime-startTime)+"ms");
        
		//hashmap查询
        long startTime1 = System.currentTimeMillis();
        HashMap<String, List<Person>> hm = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            if(!hm.containsKey(list.get(i).name)){//如果hashMap不包含这个名字的对象
                hm.put(list.get(i).name,new ArrayList<>());//新建一个元素
            }
            hm.get(list.get(i).name).add(list.get(i));//获取这个key对应的值(列表),add这个对象
        }
        int result = 0;
        if(hm.containsKey("person-5555")){
            result =  hm.get("person-5555").size();
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println("找到名称为person-5555的数量:"+result);
        System.out.println("for循环总共用时:"+(endTime1-startTime1)+"ms");

    }
}

8)比较字符串

​ 创建一个长度是100的字符串数组,使用长度是2的随机字符填充该字符串数组,统计这个字符串数组里重复的字符串有多少种,使用HashSet来解决这个问题

import java.util.HashMap;
import java.util.HashSet;

public class Exercise {
    public static void main(String[] args) {
        String cons = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String[] str = new String[100];
        for (int i = 0; i < str.length; i++) {
            str[i] = cons.charAt((int)(Math.random()*62))+""+cons.charAt((int)(Math.random()*62));
        }
        for (int i = 0; i < str.length; i++) {
            if(i%10==0 && i!=0){
                System.out.println();
            }
            System.out.print(str[i] +"  ");
        }
        System.out.println();
        HashSet<String> hs = new HashSet<>();
        HashMap<String,Integer> hm = new HashMap<>();//存放重复的字符和个数
        for (int i = 0; i < str.length; i++) {
            if(!hs.add(str[i])){
                if(hm.containsKey(str[i])){
                    hm.put(str[i], hm.get(str[i])+1);
                }else{
                    hm.put(str[i], 2);
                }

            }
        }
        System.out.println("重复字符串及其个数:"+hm);
    }
}

9)统计概率

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

import java.util.*;

public class Exercise {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        int count= 0;
        int number = 1000000;
        for (int i = 0; i < number; i++) {
            Collections.shuffle(list);
            if(list.get(0)==3 && list.get(1)==1 && list.get(2)==4){
                count++;
            }
        }
        double probability = ((double)count/number);
        System.out.println(count);
        System.out.println("概率为:"+ probability*100+"%");

    }
}

10)不重复的随机数

​ 生成50个 0-9999之间的随机数,要求不能有重复的(利用set的不重复性)

import java.util.*;

public class Exercise {
    public static void main(String[] args) {
        HashSet<Integer> hs = new HashSet<>();
        while (hs.size()<50){
            hs.add((int)(Math.random()*9999));
        }
        System.out.println(hs.size());
    }
}

11)插入数据性能比较

​ 插入十万个数据,ArrayList和Linkedlist插入数据性能比较

import java.util.*;

/***
 * ArrayList插入开头花费时间为:1484ms
 * LinkedList插入开头花费时间为:16ms
 * ArrayList插入中间花费时间为:2078ms
 * LinkedList插入中间花费时间为:33466ms
 * ArrayList插入末尾花费时间为:16ms
 * LinkedList插入末尾花费时间为:0ms
 */
public class Exercise {
    //linkedlist在中间插入的耗时与插入位置有关系,用add插入时如果位置比较靠前linkedlist会比较快
    public static void main(String[] args) {
        List<Integer> alist = new ArrayList<>();
        List<Integer> llist = new LinkedList<>();
        //初始化
        for (int i = 0; i < 50; i++) {
            alist.add(i);
            llist.add(i);
        }
        test(alist,"ArrayList",0);
        test(llist,"LinkedList",0);
        test(alist,"ArrayList",1);
        test(llist,"LinkedList",1);
        test(alist,"ArrayList",2);
        test(llist,"LinkedList",2);
    }
    public static void test(List<Integer> list,String type,int cas){
        long startTime1 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            switch (cas){
                case 0:
                    list.add(1, i);
                    break;
                case 1:
                    list.add(list.size()/2, i);
                    break;
                case 2:
                    list.add(i);
                    break;
            }
        }
        long endTime1 = System.currentTimeMillis();
        String[] str = {"开头","中间","末尾"};
        System.out.println(type+"插入"+str[cas]+"花费时间为:"+ (endTime1-startTime1)+"ms");
    }
}

12)定位数据比较

​ ArrayList和Linkedlist总长度都是100000,定位到50000的位置,取出数据并且+1,再放回去,重复100000遍,比较两个的效率。

import java.util.*;

/***
 * ArrayList定位数据花费时间为:31ms
 * LinkedList定位数据花费时间为:41656ms
 */
public class Exercise {
    //linkedlist在中间插入的耗时与插入位置有关系,用add插入时如果位置比较靠前linkedlist会比较快
    public static void main(String[] args) {
        List<Integer> alist = new ArrayList<>();
        List<Integer> llist = new LinkedList<>();
        //初始化
        for (int i = 0; i < 100000; i++) {
            alist.add(i);
            llist.add(i);
        }
        test(alist,"ArrayList");
        test(llist,"LinkedList");
    }
    public static void test(List<Integer> list,String type){
        long startTime1 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
          list.set(50000,list.get(50000)+1);
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println(type+"定位数据花费时间为:"+ (endTime1-startTime1)+"ms");
    }
}

13)反转key和value

​ 使用键值对,初始化一个HashMap

​ 对这个HashMap进行反转,key变成value,value变成key

​ 提示: keySet()可以获取所有的key, values()可以获取所有的value

import java.util.HashMap;
import java.util.Iterator;

public class Exercise {
    public static void main(String[] args) {
        HashMap<String,String> hm = new HashMap<>();
        HashMap<String,String> hm1 = new HashMap<>();//迭代过程中不能更改原map,所以新new一个
        hm.put("张三","18");
        hm.put("刘海","20");
        hm.put("泰山","56");
        hm.put("觅海","33");
        Iterator<String> key =  hm.keySet().iterator();
        Iterator<String> values =  hm.values().iterator();
        System.out.println(hm);
        while (key.hasNext()){
            hm1.put(values.next(),key.next());
        }
        hm = hm1;
        System.out.println(hm);
    }
}

14)既不重复,又有顺序

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

import java.util.LinkedHashSet;

public class Exercise {
    public static void main(String[] args) {
        String s = String.valueOf(Math.PI);
        s = s.replace(".","");//去掉字符.字
        LinkedHashSet<String> lhs = new LinkedHashSet<>();
        for (int i = 0; i < s.length(); i++) {
            lhs.add(s.substring(i,i+1));
        }
        for (String str : lhs) {
            System.out.print(str + "  ");
        }
    }
}

15)自定义字符串的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获取的值分别是多少

import java.util.Arrays;

public class Exercise {
    public static void main(String[] args) {
        //随机生成长度是2-10的不等的100个字符串
        String cons = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String[] str = new String[100];
        Arrays.fill(str, "");
        for (int i = 0; i < str.length; i++) {
            for (int j = 0; j < (int)(Math.random()*9)+2; j++) {
                str[i] =str[i]+ cons.charAt((int)(Math.random()*62));
            }
        }
        for (String s : str) {
            System.out.format("%-10s的hashcode为:%d\n",s,hashcode(s));
        }
    }
    public static int hashcode(String str){
        int code = 0;
        if(str.length() == 0){
            return 0;
        }else {
            for (char c : str.toCharArray()) {
                code = code+c;
            }
            code= code*23;
            if(code>=2000){
                code = code%2000;
            }
            if (code<0){
                code = Math.abs(code);
            }
            return code;
        }
    }
}

16)自定义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,找到匹配的,把键值对的值,返回出 去。找不到匹配的,就返回空

Exercise.java

public class Exercise {
    public static void main(String[] args) {
        MyHashMap mhm = new MyHashMap();
        //这两个key的hashcode一样,所以存在同一个列表里
        mhm.put("y4",new Person("你好"));
        mhm.put("z3",new Person("你好2"));
        System.out.println(mhm.get("y4"));

    }
}

IHashMap.java

public interface IHashMap {
    public void put(String key,Object object);
    public Object get(String key);
}

MyHashMap.java

import java.util.LinkedList;

public class MyHashMap implements IHashMap{
    public LinkedList<Entry>[] ObjectArray = new LinkedList[2000];
    @Override
    public void put(String key, Object object) {
        Entry entry = new Entry(key,object);

        if(ObjectArray[hashcode(key)]==null){
            LinkedList<Entry> linkedList = new LinkedList<>();
            linkedList.add(entry);
            ObjectArray[hashcode(key)] = linkedList;
        }else {
            ObjectArray[hashcode(key)].add(entry);
        }

    }
    @Override
    public Object get(String key) {
        if (ObjectArray[hashcode(key)]==null){
            return null;
        }
        for (Entry e  : ObjectArray[hashcode(key)]) {
            if(e.key.equals(key)) {
                return e;
            }
        }
        return null;
    }
    public static int hashcode(String str){
        int code = 0;
        if(str.length() == 0){
            return 0;
        }else {
            for (char c : str.toCharArray()) {
                code = code+c;
            }
            code= code*23;
            if(code>=2000){
                code = code%2000;
            }
            if (code<0){
                code = Math.abs(code);
            }
            return code;
        }
    }
}

Entry.java

public class Entry {
    public Object key;
    public Object value;

    public Entry(Object key, Object value) {
        super();
        this.key = key;
        this.value = value;
    }
    @Override
    public String toString() {
        return "[key=" + key + ", value=" + value + "]";
    }
}

17)内容查找性能比较

​ 重复前面的 练习-查找内容性能比较 ,不过不使用HashMap,而是使用上个练习中自定义的MyHashMap

import java.util.ArrayList;
import java.util.List;
/***
 * MyHashMap的get方法返回的是一个Object类型
 * 将这个类型转换为list类型费了一点功夫
 */
public class Exercise {
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<>();
        for (int i = 0; i < 3000000;i++) {
            list.add(new Person("person-"+((int)(Math.random()*9000)+1000)));
        }

        //for循环查询
        long startTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < list.size(); i++) {
            if(list.get(i).name.equals("person-5555")){
                count++;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("找到名称为person-5555的数量:"+count);
        System.out.println("for循环总共用时:"+(endTime-startTime)+"ms");

        //hashmap查询
        long startTime1 = System.currentTimeMillis();
        MyHashMap hm = new MyHashMap();
        for (Person person : list) {
            List ls= (List) hm.get(person.name);

            if (ls == null) {//如果hashMap不包含这个名字的对象
                ls = new ArrayList<>();
                hm.put(person.name, ls);//新建一个元素
            }
            ls.add(person);//获取这个key对应的值(列表),add这个对象
        }
        int result = 0;
        List lss= (List) hm.get("person-5555");
        if(lss != null){
            result =  lss.size();
        }
        long endTime1 = System.currentTimeMillis();
        System.out.println("找到名称为person-5555的数量:"+result);
        System.out.println("for循环总共用时:"+(endTime1-startTime1)+"ms");

    }
}

18)自定义顺序的TreeSet

​ 默认情况下,TreeSet中的数据是从小到大排序的,不过TreeSet的构造方法支持传入一个Comparator

​ 通过这个构造方法创建一个TreeSet,使得其中的的数字是倒排序的

import java.util.Comparator;
import java.util.TreeSet;

public class Exercise {
    public static void main(String[] args) {
        Comparator cp = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if((Integer)o1 <(Integer)o2){
                    return 1;
                }else {
                    return -1;
                }
            }
        };
        TreeSet<Integer> ts = new TreeSet<>(cp);
        ts.add(25);
        ts.add(10);
        ts.add(64);
        ts.add(23);
        System.out.println(ts);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值