Java高级---集合

Java高级—集合

首先建议看一下前一章的集合框架
从这一章开始,我们正式进入Java高级技术部分

集合:Collection 无序可重复

一、List :有序,可重复

1、ArrayList:一维数组作为底层存储
特点:遍历、随机访问快、修改方便,其他慢

	这里讲一下插入方法:add(Obj)和add(1,Obj)
	
	插入数据元素的底层实现原理:【这里建议看Java源码】
	插入前判断容量是否越界,如果越界,则扩容。
	扩容后比数组最大长度大,则是int最大值
	没有达到数组最大长度,就是数组最大长度
	正常情况:扩容1.5倍,如果不够,则直接把当前长度赋值给数组新长度
	如果是新数组,与10比较,比10大,大的直接赋值给数组长度
	不需要扩容或扩容完成则直接插入

那接下来,我们看一下ArrayList如何使用:
1.插入

ArrayList a = new ArrayList();//声明并分配空间

//元素的插入
a.add("abc");
a.add("123");
a.add("唯颂科技");
a.add("2s留#%");
a.add("abc");
a.add(1,"shall");		//指定位置插入,元素后移

System.out.println(a);//因为重写了toString方法,所以可以直接输出

结果显示
在这里插入图片描述
2.删除

 if(a.contains("123")) {
     a.remove("123");//删除对象
 }
 a.remove(1);//删除指定下标元素,下标从0开始

两种方式删除都可以,如果list中没有你要删除的元素,你删除了也不会报错

3.查找

a.get(2);		//只有根据下标指定查找元素一个查询方法

4.修改

a.set(1,"溜溜球");	//指定位置修改

5.输出(常见的三种输出方式

//1.普通for
for (int i = 0; i < a.size(); i++) {
    System.out.println(a.get(i));//获取当前元素
}
//2.增强for
for(Object s:a){    //由于上面一开始没有定义类型,所以这里写了Object
    System.out.println(s);
}
//3.迭代器
Iterator it = a.iterator();
while (it.hasNext()){
    System.out.println(it.next());//一次循环只能使用一次next(),不然会出错
    //如果要用多次,赋值给变量即可
}

ArrayList常用方法
在这里插入图片描述
2、LinkedList:以链表作为底层存储
特点:增加、删除快,其他慢
其实有点数据结构基础的都知道这二者的特点

LinkedList中的方法和ArrayList几乎一样,只不过多一些头结点、尾节点的操作方法
那些一样的我就不写了,这里主要讲一下没有的:
在这里插入图片描述
然后:
push:在头结点处插入元素

//jdk1.8源码
 public void push(E e) {
        addFirst(e);
    }

pop:删除头结点

//jdk1.8源码
public E pop() {
        return removeFirst();
    }

peek:返回头结点

//jdk1.8源码
public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    /**
     * Retrieves, but does not remove, the head (first element) of this list.
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */

peekFirst:返回头结点
peekLast:返回尾结点

poll:返回头结点,只不过是unlinked

//jdk1.8源码
public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
    /**
     * Retrieves and removes the head (first element) of this list.
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */

pollFirst:返回头结点
pollLast:返回尾结点

offer:插入元素

//jdk1.8源码
public boolean offer(E e) {
        return add(e);	//其实就是插入
    }

offerFirst:在头结点插入元素
offerLast:在尾结点插入元素

是不是感觉LinkedList里面一堆功能重复的方法

3、Vector:ArrayList的线程安全版本
由于使用比较少,所以在这里就不赘述了
------------------------------------------------------------------------------------------------------
最后,做个小小的练习:
Book类

/* 定义图书类Book,具有属性账号id,书名name、作者author 和价格price,
 * 在创建图书对象时要求通过构造器进行创建,一次性将四个属性全部赋值,
 * 1)要求账户属性是int型,名称是String型,作者是String型,价格是double,
 * 请合理进行封装。
 * 2)在Book类,添加toString方法,要求返回 图书信息字符串,使用\t隔开各信息
 * 3)要求定义一个图书馆Library类,在图书馆类中添加一个集合用于保存多本图书
 * 4)在图书馆类中要求能够新增图书
 * 5)在图书馆类中要求可以查看所有添加过的图书
 * 6)不允许添加重复的图书(如果账号id和书名name相同,则认为两本书是相同的)
 * (重写equals方法)
 */
public class Book {
    private int id;
    private String name;
    private String author;
    private double price;

    public Book(int id, String name, String author, double price) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "书籍编号=" + id +"\t"+
                "书籍名称=" + name + "\t"+
                "书籍作者=" + author + "\t" +
                "书籍价格=" + price +
                '}'+"\n";
    }

    public boolean equals(Book b){
        if(null==this.name||0>=this.id||b.getId()<=0||null==b.getName()){
            return false;
        }
        return this.name.equals(b.getName())&&this.id==b.getId();
    }
}

Library类

public class Library {
    public static void main(String[] args) {
        List<Book> list = new ArrayList();
        Scanner sc = new Scanner(System.in);
        String s = "";
        do{
            System.out.println("需要添加书籍吗?(y/n)");
            s = sc.next();
            if("y".equals(s)){
                System.out.println("请输入图书编号:");
                int id = sc.nextInt();
                System.out.println("请输入图书名称:");
                String name = sc.next();
                System.out.println("请输入图书作者:");
                String author = sc.next();
                System.out.println("请输入图书价格:");
                double price = sc.nextDouble();
                Book temp = new Book(id,name,author,price);

                if(list.size()==0){     //为空时,直接插入
                    list.add(temp);
                }

                int flag = 1;
                for (int i = 0; i < list.size(); i++) {
                    if(!list.get(i).equals(temp)) {//不同
                        flag = 2;
                    }else {
                        flag = 1;
                        break;
                    } //相同

                }
                if(flag==2)
                    list.add(temp);
            }else
                break;
        }while ("y".equals(s));
        System.out.println(list);
    }
}

结果显示
在这里插入图片描述

二、Set :无序,不可重复

1、HashSet:是以HashMap的key值的Hash码来进行存储(保证唯一,理论上)
Set中的方法和List几乎一样,所以就不写了。这里就看一次add方法
可以点进Set的add方法看一看
在这里插入图片描述
具体使用

HashSet hashSet = new HashSet();	//先不使用泛型
hashSet.add("abc");
hashSet.add(123);
hashSet.add("123");
hashSet.add("123");					//add源码是使用equals进行比较,只输出一个
hashSet.add(new String("123"));
System.out.println(hashSet);
hashSet.remove("123");
System.out.println(hashSet);
Iterator it = hashSet.iterator();   //这里不能是用fori形式,可以foreach,因为set没有下标
while (it.hasNext()){
    System.out.println(it.next());
}
ArrayList al = new ArrayList();
al.addAll(hashSet);//把set扔到list中

结果显示
在这里插入图片描述

2、TreeSet是一个有序的集合,它的作用是提供有序的Set集合。它继承了AbstractSet抽象类,实现了NavigableSet,Cloneable,Serializable接口。TreeSet是基于TreeMap实现的,TreeSet的元素支持2种排序方式:自然排序或者根据提供的Comparator进行排序

TreeSet的底层实际使用的存储容器就是TreeMap。对于TreeMap而言,它采用一种被称为”红黑树”的排序二叉树来保存Map中每个Entry。每个Entry被当成”红黑树”的一个节点来对待。

这里使用就和上面的HashSet一样,所以就不写了。

三、Map:键值对

key不能重复,value可以。
包含接口Entry<K,V>
在HashMap中使用Node结点实现Entry键值对
Node结点用next属性实现单链表

Map底层数组+单向链表,1.8之后加入红黑树【红黑树是一种自平衡二叉树,通过左旋和右旋进行平衡】

数组+链表—>红黑树 的条件
一个是链表的长度达到8个,一个是数组的长度达到64个,转用红黑树进行底层存储

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

为什么是节点数到8个就转成红黑树?
官方解释:理想情况下使用随机的哈希码,容器中节点分布在hash桶中的频率遵循泊松分布,按照泊松分布的计算公式计算出了桶中元素个数和概率的对照表,可以看到链表中元素个数为8时的概率已经非常小,再多的就更少了,所以原作者在选择链表元素个数时选择了8,是根据概率统计而选择的

【Tips:泊松分布】
参考链接:http://www.math.wm.edu/~leemis/2008amstat.pdf
参考链接:https://mp.weixin.qq.com/s/IurrCgtO6ggmfby3oDByng

泊松分布(Poisson)、二项式分布都属于 离散分布
正态分布(Normal)属于 连续分布

二项式分布,样本无穷大—>泊松分布,样本无穷大—>正态分布
在这里插入图片描述
为什么使用红黑树
因为不仅查询速度快,虽然不及平衡二叉树,但是也没差多少,相反,插入删除的效率却大大优于平衡二叉树

Map中HashMap(不安全,会死循环,但是快)最为典型,至于什么HashTable(线程安全,但是慢),concurrentHashMap【分段锁,解决二者问题】等这里不重点讲了。

HashMap
默认容量:16,负载因子:0.75
首先是方法,和List几乎一样,只不过插入和修改不同,在Map中插入和修改都是put,put相同key的时候,value就会覆盖,原来内容会因为失去指向而在内存当中,等待Java垃圾回收机制清理。

具体可以看下面代码:

public class TestHashMap {
    public static void main(String[] args) {
        HashMap<Integer,String> map = new HashMap(); //注意泛型,这里写不写无所谓
        map.put(1,"zxx");        //增加:key-value
        map.put(3,"Jad");

        map.put(3,"厉害的感觉");  //修改:一个key只能有一个value
        map.replace(1,"Lif");    //修改,本质还是put,可以进去看源码

        map.remove(2);      //删除
        map.remove(2,"dg");

        System.out.println(map.get(3));                //输出
        System.out.println(map);

        //4种遍历
        Iterator it = map.entrySet().iterator();      //键值对:entrySet
        while (it.hasNext()){
            System.out.println(it.next());
        }

        Iterator iter = map.keySet().iterator();      //key
        while (iter.hasNext()){
            System.out.println(iter.next());
        }

        Iterator iters = map.values().iterator();     //value
        while (iters.hasNext()){
            System.out.println(iters.next());
        }
        
		map.forEach((key,value)->{					 //lamba表达式
            System.out.println(key);
            System.out.println(value);
        });
    }
}

【注意:keySet返回的是Set类型,value返回的是Collection类型】

Map常用方法
在这里插入图片描述
举个例子:
要求:
学员应聘至外企工作,每个学员都会有一个英文名称,对应该学员对象。请实现通过英文名称,获得该学员对象的详细信息
学员属性包括姓名、性别、成绩

学生类:

public class Student  implements Comparable<Student>{
    private String name;
    private String gender;
    private int score;

    public Student(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }

    public Student(String name, String gender, int score) {
        this.name = name;
        this.gender = gender;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public double getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return  "姓名:" + name + "\t" +
                "性别:" + gender + "\t"+
                "成绩:" + score ;
    }
}

测试类:

public class TestStudent {
    public static void main(String[] args) {
        Student s1 = new Student("Jack","男",98);
        Student s2 = new Student("Alis","女",87);
        Student s3 = new Student("Louis","女",59);
        Student s4 = new Student("Tom","男",88);
        
        HashMap<String,Student> map = new HashMap();
        map.put("杰克",s1);
        map.put("爱丽丝",s2);
        map.put("路易斯",s3);
        map.put("汤姆",s4);
        
//2中输出方式
//        Collection c = map.values();
//        for(Object s: c){
//            System.out.println(s);
//        }

//        Scanner sc  = new Scanner(System.in);     //按输入进行输出
//        System.out.println("请输入名称");
//        String name = sc.next();
//        if(map.containsKey(name)) {
//            System.out.println(name+map.get(name).toString());
//        }else {
//            System.out.println("抱歉没有这个人");
//        }
    }
}

Map怎么通过Value进行比较呢?

通过转成其他类型的集合,例如转成List,自定义比较器,利用Collections.sort(List,Comparator)进行排序

/**
 * @Author shall潇
 * @Date 2021/1/29
 * @Description     由于Collections类只给list,set用,Map不能直接用,所以得转成list
 */
public class MapValueSort {
    public static void main(String[] args) {
        Map<String,Integer> map = new TreeMap<>();
        
        //插入数据
        map.put("zzz",23);
        map.put("Tom",53);
        map.put("Jack",63);
        map.put("Luis",27);
        map.put("Shall",34);

        //自定义比较规则
        Comparator<Map.Entry<String,Integer>> comparator = new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                return o1.getValue()-o2.getValue();   //年龄升序
            }
        };
        //放入List
        List<Map.Entry<String,Integer>> list = new LinkedList(map.entrySet());
        Collections.sort(list,comparator);  //两个参数:list集合,比较器对象
		//输出
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i).getKey()+" = "+list.get(i).getValue()+"\t");
        }
    }
}

结果显示
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值