javaSE-集合

一.类图

collection

 

上图中的Iterator接口在1.5之后就由Iterable接口代替了,Iterable接口封装了一个方法叫做iterator,该方法返回一个iterator对象,而每个iterator接口的实现类都分别写在了各个集合类的实现类中,以内部类方式实现iterator接口

用ArrayList举个例子:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    //实现了iterator接口的内部类
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {} 
    }
    
    //实现了Iterable接口中的iteartor方法
    public Iterator<E> iterator() {
        return new Itr();
    }
}


map

 

二.Collection接口

1.概述

该接口是所有单元素存储的总接口(List接口和Set接口的父接口),那个Iterable可以理解为Collection的一个附属接口,虽然它是Collection的父接口,但是整个集合的主要功能定义都是在Collection中定义的,Iterable这个接口就提供了个iterator()方法供所有实现类获取迭代器.所以Collection是核心接口

接下来看看它提供的方法:

2.Collection接口中的方法


三.List接口及其实现类

1.概述

List是有序可重复的,底层的数据结构是线性结构

 2.方法

List不单单继承了Collection的方法,还增加了一些新的方法。


3.实现类

ArrayList : 底层数据结构是数组,查询和修改效率高,删除添加效率低

 LinkedList : 底层数据结构是双向链表,删除添加效率高,查询和修改效率低

Vector : 类似于ArrayList,它是线程安全的,但是现在一般线程安全问题都另外处理了,所以这个也就被弃用了

Stack: 继承Vector实现的栈,栈结构是先进后出,但已被LinkedList取代

4.LinkedList新增方法

四.Set接口及其实现类

1.概述

Set是无序不可重复的,其中无序是指添加的顺序和取出来的顺序无序,存储的不是线性结构

 2.方法


3.实现类

HashSet :

它的底层是由HashMap实现的,这个map是一个仅有key的map(value是一个常量对象,用来占位,没有什么意义)

当这个key重复的时候,add方法不会添加这个重复的元素

TreeSet

它的底层是由TreeMap实现的,可排序,默认自然升序.

五.Collections工具类和Comparable、Comparator比较器

1.Collections工具类

Collections是一个包装工具类。它包含有各种有关集合操作的静态多态方法,此类不能实例化,服务于Java的Collection接口。

常用方法:

sort、reverse、fill、copy、max、min、swap等

重点: sort排序

public static <T extends Comparable<? super T>> void sort(List<T> list)


这个方法的意思就是sort传入的是一个List接口下的实现类对象,而且这个泛型还要求要比较的元素必须是实现了Comparable这个接口或者是它的子类

根据元素的自然顺序 对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable 接口。
此外,列表中的所有元素都必须是可相互比较的(也就是说,对于列表中的任何 e1 和 e2 元素,e1.compareTo(e2) 不得抛出 ClassCastException)。

2.Comparable、Comparator比较器

对象排序,就是比较大小,要实现Comparable或Comparator比较器之一,才有资格做比较排序。
【介绍】

Comparable:与对象紧相关的比较器,可以称“第一方比较器”。
Comparator:此为与具体类无关的第三方比较器。
例子:

定义一个学生类,实现Comparable接口,使用学号进行Collections.sort排序

定义一个学生类,实现Comparator接口,使用学号进行Collections.sort排序

学生类

package test06;

public class Student implements Comparable<Student>{
    private String name;
    private String sex;
    private int age;
    private int stuNo;
    @Override
    public int compareTo(Student o) {
        // TODO Auto-generated method stub
        //按照学号升序排序
//        return this.stuNo-o.stuNo;
        //降序
//        return o.stuNo-this.stuNo;
        //多条件排序(首先按照年龄升序,然后按照学号降序)
//        if(this.age-o.age==0){
//            return o.stuNo-this.stuNo;
//        }
//        return this.age-o.age;
        //按照姓名排序
        return this.name.compareTo(o.name);
    }

    public Student() {
        super();
    }

    public Student(String name, String sex, int age, int stuNo) {
        super();
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.stuNo = stuNo;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Student [name=" + name + ", sex=" + sex + ", age=" + age
                + ", stuNo=" + stuNo + "]";
    }

}

测试类:

public class TestComparable {
    public static void main(String[] args) {
        //创建四个学生
        Student s1 = new Student("tom","男",20,1807001);
        Student s2 = new Student("jack","男",20,1807022);
        Student s3 = new Student("jeny","女",22,1807011);
        Student s4 = new Student("lady","女",21,1807031);
        
        List<Student> stuList = new ArrayList<Student>();
        stuList.add(s1);
        stuList.add(s2);
        stuList.add(s3);
        stuList.add(s4);
        System.out.println("排序前=====");
        for(Student s:stuList){
            System.out.println(s);
        }
        Collections.sort(stuList);
        System.out.println("排序后=====");
        for(Student s:stuList){
            System.out.println(s);
        }
    }
}

输出结果:

排序前=====
Student [name=tom, sex=男, age=20, stuNo=1807001]
Student [name=jack, sex=男, age=20, stuNo=1807022]
Student [name=jeny, sex=女, age=22, stuNo=1807011]
Student [name=lady, sex=女, age=21, stuNo=1807031]
排序后=====
Student [name=jack, sex=男, age=20, stuNo=1807022]
Student [name=jeny, sex=女, age=22, stuNo=1807011]
Student [name=lady, sex=女, age=21, stuNo=1807031]
Student [name=tom, sex=男, age=20, stuNo=1807001]

六.Map接口

1.概述

Map用于保存具有映射关系的数据,因此Map集合里保存两组值。

一组值用于保存key,一组值用于保存value

key~value之间存在单向一对一关系,通过指定key可以找到唯一的value值

key和value都可以是任何引用类型对象

允许存在value为null,但是只允许存在一个key为null

2.常用方法


七.HashMap类

1.概述

特点:key无序不可重复,底层是哈希表

2.哈希表实现原理

HashMap实际上是一个"链表的数组"的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。

当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

只有相同的hash值的两个值才会被放到数组中的同一个位置上形成链表。

如果这两个Entry的key通过equals比较返回true,新添加Entry的value将覆盖集合中原有Entry的value,但key不会覆盖。

 

3.总结

HashMap中key的hashCode值决定了<k,v>键值对组成的entry在哈希表中的存放位置。
HashMap中key通过equals()比较,确定是覆盖key对应的value值还是在链表中添加新的entry。
综合前两条,需要重写hashCode()和equals()方法。

八.TreeMap类

1.概述

TreeMap是SortedMap接口的实现类,可以根据Key进行排序,HashMap没有这个功能。
【特点】

底层由可排序二叉树实现

不指定比较器默认按照key自然升序,指定比较器按照比较器排序

2.遍历TreeMap的两种方法

使用KeySet

//使用keySet方式输出
Set<Integer> keySet = treemap.keySet();
        for(Integer i: keySet){
            System.out.println("key--->"+i+"   value--->"+treemap.get(i));
        }

使用EntrySet

//使用entrySet方式输出
        Set<Map.Entry<Integer, Student>> entryset = treemap.entrySet();
        for(Map.Entry<Integer, Student> entry:entryset){
            System.out.println("key--->"+entry.getKey()+"   value--->"+entry.getValue());
        }

区别:

使用KeySet获取的是TreeMap中所有的key

使用EntrySet获取的是TreeMap中所有的键值对

3.为什么keySet和entryset输出后就是排好序的

package day23;

import java.util.*;


/**
 * 作业2:treemap的keyset方式或是entryset方式如何保证的有序输出。
 * @author  付君华
 * @Date 2021年6月29日
 * @Description
 */
public class Hw02 {
    
    /*
     分析keySet
     public Set<K> keySet() {
        return navigableKeySet();
     }
     上面的调用了下面的方法
     public NavigableSet<K> navigableKeySet() {
        KeySet<K> nks = navigableKeySet;
        return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
    }
    返回了一个NavigableSet 这个是SortedSet下的一个子接口 而这个KeySet是NavigableSet的实现类
    
    判断nks是否为空,非空的话说明创建过了,不用重复出创建
    空的话创建一个KeySet
     看看这个KeySet具体代码    
     
     
      
      static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
        private final NavigableMap<E, ?> m;
        KeySet(NavigableMap<E,?> map) { m = map; }

        public Iterator<E> iterator() {
            if (m instanceof TreeMap)
                return ((TreeMap<E,?>)m).keyIterator();
            else
                return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
        }
    }
    
    主要看这个构造方法
    static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
        private final NavigableMap<E, ?> m;
        KeySet(NavigableMap<E,?> map) { m = map; }
      
      
     首先是传入了个map,这个map也就是treemap,也就是调用keySet方法的的treemap
     
     但是也没有进行排序啊,就创建了个对象,咋就排序了呢?
     百度查了一下资料,网上说是在遍历的时候才会进行排序
     so 嘎
     那如果用普通for循环的方式来遍历是否就不会进行排序了呢?
     
     不行,这KeySet中就没get方法,只能用增强for循环,只要一用这个增强for,那就会调用KeySet中的迭代器
     然后进行排序,返回迭代器咋就排序了呢? 
     两种可能,一是for循环给实现的,而是迭代器实现的
     找一下增强for的源码:
     for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
           i = (Integer)iterator.next();        
     }
     很明显不是for实现的
     那只能是迭代器实现的了
     
     那看看迭代器
     
     上面for中调用的是无参的迭代器方法,所以应该是
      
      public Iterator<E> iterator() {
            if (m instanceof TreeMap)
                return ((TreeMap<E,?>)m).keyIterator();
            else
                return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
        }
        
      如果是个treemap 那么就返回一个treemap 的iterator 否则返回那个玩意的iterator
      
      那再看看keyIterator
      Iterator<K> keyIterator() {
        return new KeyIterator(getFirstEntry());
      }
      
      final class KeyIterator extends PrivateEntryIterator<K> {
        KeyIterator(Entry<K,V> first) {
            super(first);
        }
        public K next() {
            return nextEntry().key;
        }
       }
       
       final Entry<K,V> getFirstEntry() {
        Entry<K,V> p = root;
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
      }
      
      也就是将第一个entry传入到了KeyIterator的构造方法中
      这个构造方法将这个传到了父类的构造中
      
      PrivateEntryIterator(Entry<K,V> first) {
            expectedModCount = modCount;
            lastReturned = null;
            next = first;
        }
        
      应该快找到答案了
      
      主要看看这个类中的实现
      
      
      abstract class PrivateEntryIterator<T> implements Iterator<T> {
        Entry<K,V> next;
        Entry<K,V> lastReturned;
        int expectedModCount;

        PrivateEntryIterator(Entry<K,V> first) {
            expectedModCount = modCount;
            lastReturned = null;
            next = first;
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry<K,V> nextEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = successor(e);
            lastReturned = e;
            return e;
        }

        final Entry<K,V> prevEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = predecessor(e);
            lastReturned = e;
            return e;
        }

        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            // deleted entries are replaced by their successors
            if (lastReturned.left != null && lastReturned.right != null)
                next = lastReturned;
            deleteEntry(lastReturned);
            expectedModCount = modCount;
            lastReturned = null;
        }
    }
    
    主要看他的next方法,怎么就能排序输出呢?
    
    next = successor(e);
    
    这里调用了一个方法
    
    static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
        if (t == null)
            return null;
        else if (t.right != null) {
            Entry<K,V> p = t.right;
            while (p.left != null)
                p = p.left;
            return p;
        } else {
            Entry<K,V> p = t.parent;
            Entry<K,V> ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }
    
    这个百度了一下是获取后继节点的意思,但是也没实现排序啊
    
    ..
    恍然大悟
    在创建treemap的时候就已经是排好序的了(红黑二叉树)
    然后获取节点的时候就是有序获取的,所以这个keySet就是有序的
    
    那entry也就是一样的道理了
    
    
    总结一下:
    
    keySet和entrySet方法返回了一个set集合
    
    这个set集合中的元素在创建treemap的时候就已经排好序了
    
    上面的方法在调用的时候用到了treemap,然后这俩方法就将这个map保存到了一个内部类中
    
    在遍历的时候,调用迭代器,然后将这个treemap的一个一个值给输出   
      
     */
    
    
    
    
    public static void main(String[] args) {
        //创建一个TreeMap
        Map<Integer, Student> treemap = new TreeMap<Integer, Hw02.Student>();
        //创建学生数据
        Student s1 = new Student("zhagnsan", 20, 2021001);
        Student s2 = new Student("lisi", 20, 2021002);
        Student s3 = new Student("wangwu", 20, 2021003);
        //将数据添加到Map中
        treemap.put(s1.getStdNo(),s1);
        treemap.put(s2.getStdNo(),s2);
        treemap.put(s3.getStdNo(),s3);
        //使用keySet方式输出
        Set<Integer> keySet = treemap.keySet();
        for(Integer i: keySet){
            System.out.println("key--->"+i+"   value--->"+treemap.get(i));
        }
        
        //使用entrySet方式输出
        Set<Map.Entry<Integer, Student>> entryset = treemap.entrySet();
        for(Map.Entry<Integer, Student> entry:entryset){
            System.out.println("key--->"+entry.getKey()+"   value--->"+entry.getValue());
        }
        
        
    }
    
    public static class Student {
        private String name;
        private int age;
        private int stdNo;
        
        @Override
        public int hashCode() {
            return Integer.valueOf(this.stdNo).hashCode()+this.name.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            Student stu=(Student)obj;
            return (this.stdNo==stu.stdNo)&&(this.name.equals(stu.name));
        }

        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + ", stdNo="
                    + stdNo + "]";
        }

        public Student(String name, int age, int stdNo) {
            super();
            this.name = name;
            this.age = age;
            this.stdNo = stdNo;
        }

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public int getStdNo() {
            return stdNo;
        }

        public void setStdNo(int stdNo) {
            this.stdNo = stdNo;
        }

    }

}


九.泛型

1.概述

泛型就是用来约束数据类型的一种语法,尤其是在集合与框架编写中常见

2.泛型的简单使用

List<String> list = new ArrayList<String>();//这个集合中就只能存储String类型

3.泛型的拓展使用

//在类上加泛型
class Stu<T>{
    public void m1(T t){
        System.out.println(t);
    }
}
//测试
class Main{
    public static void main(String args[]){
        //创建一个Stu类,带泛型
        Stu<String> s = new Stu<String>();
        //调用Stu中的m1方法
        s.m1("hello");//这只能传字符串
    }
}


//在方法上加泛型
public class Test {
    public static void main(String[] args) {
        m1("123");//输出123,这里传入什么类型都可以,相当于Object
    }
    
    public static <T> void m1(T t){
        
        System.out.println(t);
        
    }
}

4.extends关键字和super关键字

<T extends 类型 > 就是T是这个类型或子类

<T super 类型> 就是这个这个类型或父类

5.?符号

? 是指所有没有限定类型,T是限定了某一种类型

<? extends 类型> 所有这个类型或者这个类型的子类都可以
<? super 类型> 所有这个类型或者这个类型的父类
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mizui_i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值