java集合类总结

Collection体系继承树

蓝色框:接口         红色框:实现类        红色框+阴影:常用实现类

Queue:先进先出的队列。

Map体系继承树

蓝色框:接口。 红色框:实现类。 红色框+阴影:常用实现类

Map:具有映射关系(k,v)的 集合,其所有key无序且不可重复。

集合与数组对比

  1. 数组初始化后,其长度就确定了;而集合是可以动态扩展容量的。
  2. 数组一旦定义好,就只能存储指定类型数据,集合存放类型可以不是一种。
  3. 数组时java语言内置数据类型,是线性排序的,查询执行效率或者类型检查都很快,但是对于添加、删除、插入操作效率不高。

集合与数组的转换

        //数组转集合
        int[] arr = new int[]{123,12};
        List list = Arrays.asList(arr);

        //集合转数组
        Object[] objects = list.toArray();
    

也可通过循环,一个个读取然后相互进行转换。

List增删改查插与遍历

        List list = new ArrayList();
        //增
        list.add(123);
        list.add("张三");
        list.add(456);
        System.out.println(list);//[123, 张三, 456]
        //删 根据索引删除,根据值删除
        list.remove(0);
        list.remove("张三");
        System.out.println(list);//[123, 张三, 456]
        //改
        list.set(0,"李四");
        System.out.println(list);//[李四]
        //查
        Object o = list.get(0);
        System.out.println(o);//李四
        //插
        list.add(1,"asda");
        System.out.println(list);//[李四, asda]

三种方式遍历: Iterator,增强for循环、普通循环。·        

        List list = new ArrayList();
        list.add(1);
        list.add("张三");
        list.add(3.25);
        //for循环遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //增强for循环
        for (Object o: list) {
            System.out.println(o);
        }
        
        /**
         * Iterator  迭代器:默认游标在第一个元素之前
         * hasNext():是否有下一个元素
         * next():返回迭代器的下一个位置
         * remove():移除迭代器当前游标位置的元素
         */
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
            iterator.remove();
        }
        System.out.println(list);//[]

Map增删改查遍历

        Map map = new HashMap();
        //增
        map.put(1,"张三");
        System.out.println(map);//{1=张三}
        //改
        map.put(1,"李四");
        System.out.println(map);//{1=李四}
        //查,根据key值来查询
        Object o = map.get(1);
        System.out.println(o);//李四
        //删
        map.remove(1);
        System.out.println(map);//{}

遍历

获取所有的key:map.keySet()返回包含所有key的集合。

获取所有的value:map.values()返回包含所有value的集合。

获取key和value:entrySet()+迭代器。

        Map map = new HashMap();
        map.put(1,"张三");
        map.put(2,"李四");
        map.put(3,"王五");

        //获取所有的value
        Collection values = map.values();
        System.out.println(values);//[张三, 李四, 王五]

        //获取所有的key
        Set set = map.keySet();
        System.out.println(set);//[1, 2, 3]

        //entrySet
        Set entrySet = map.entrySet();
        Iterator iterator = entrySet.iterator();
        while(iterator.hasNext()){
            Object obj = iterator.next();
            Map.Entry entry = (Map.Entry)obj;
            System.out.println(entry.getKey()+"------>"+entry.getValue());
        }

List集合中:ArrayList 、LinkedList、Vector区别

ArrayList:底层使用Object数组实现;当数组容量不够时,创建一个新数组,这个新数组的容量是原数组的1.5倍,然后将原数组中的元素复制到这个新数组中。建议使用时,直接在构造方法中指定数组大小。避免扩容,影响效率。

  1. JDK7:当使用无参构造创建列表时,底层默认创建长度是10的Object数组。
  2. JDK8:当是用无参构造创建列表时,底层并不会指定数组的容量,第一次添加容量时才默认创建长度为10的数组

LinkedList: 底层使用双向链表实现;内部声明了Node类型的next、prev属性,prev指向前一个元素,next指向下一个元素------体现了双向链表,链表更占内存(多了两个引用)。

Vector:线程安全的,里面的方法基本都加了synchornized,因此效率很慢;底层使用Object数组存储;当使用无参构造创建时,JDK7和JDK8都默认创建一个长度为10的数组;当容量不够时会按照原始用量的100%比例扩容。

  1. 对于随机访问ArrayList根据下表查找,时间复杂度为O(1),而LinkedList时间复杂为O(n)。
  2. 对于插入和删除,LinkedList要优于ArrayList;ArrayList需要重新计算位置,移动大量元素,甚至扩容。

Set集合中:HashSet、TreeSet

TreeSet

TreeSet有序且不可重复,是基于TreeMap实现的;向TreeSet中添加的对象,一定要是同一类的对象(原理在TreeMap中说)。

HashSet

HashSet是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75的HashMap。它封装了一个HashMap对象来存储所有的集合元素,所有放入HashSet集合元素实际上是由HashMap的key来保存的,而HashMap的value则存储一个PRESENT,它是一个静态的Object对象。原理在HashMap中说。

public HashSet() {
    map = new HashMap<>();
}
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

不同点:

  1. HashSet中元素可以为null,但只有一个null;TreeSet中不能有null;
  2. HashSet底层使用hash表进行排列,无序;
  3. TreeSet底层用红黑树进行排列;

Map:HashMap、TreeMap

HashMap底层原理

JDK7中: 

  1. HashMap 在实例化后创建一个长度为16的Entry数组;HashMap中有一个加载因子默认值为0.75,当存储的元素超过当前数组容量的0.75时会进行扩容,这个加载因子,我们可以通过有参构造创建对象时自己设定。
  2. 在存储数据时,会先计算存储元素的key的hash值来判断其所存储的位置,当两个存储元素的hash值相等时,会调用equals方法判断两个对象是否相等。如果对象相等会进行value值的覆盖;若果对象不相等会在同一个位置以链表形式存储元素。补充说明:
    1. 我们会先调用hashCode方法进行提前校验,避免过多的调用equals方法,以提高效率。这也是为什么我们重写equals方法时要重写hashCode方法。
    2. 同一链表上key的hashCode值不一定相等,且大多数都不相等;因为他的存储方式让我们误以为相等:例如,当前HashMap的容量为16,有两个元素key的Hash值分别为1和17;这时1会被存入数组下标为1的位置,而17此时的存储位置由他的值对存储容量取模的值决定的,取模的值也是1,他们都存在1号位置。但他们hash值根本不一样。
  3. 扩容问题:当容量达到临界时,进行扩容后会重新获得元素的hash值,对hash值”一样“的采用头插法将元素添加到链表。-----会出现扩容循环链表(多线程中出现)。HashMap扩容死循环图来源于 HashMap多线程扩容导致死循环解析(JDK1.7)_Sicimike的博客-CSDN博客_hashmap多线程扩容

JDK8中:  与JDK7三点不一样

  1. HashMap 在实例化并添加元素时才创建一个长度为16的Node数组;
  2. JDK8中底层是基于数组+链表+红黑树实现的,底层维护的是Node数组;当数组长度>64且链式数据大于8时,会开启红黑树。(当链表长度过长时会影响我们的查找效率,红黑树可以提高查询效率;之所以不用B/B+树,当数据量不是很多时,数据会挤在一个节点中,这个时候遍历效率相当于退化成了链表)
  3. 扩容时采用尾插法,每次都取最后一个元素放在新的数组链表中,避免了扩容时循环链表的产生。扩容时将元素hash值与旧数组大小做与运算,新增的位为0放在原位置,为1进行移位(原位置+旧数组大小)。

        例如:原数组大小为16,存储元素中有Hash值为1和17 的两个元素。

TreeMap底层原理

基于红黑树实现。映射根据其键的自然排序进行排序。因为底层调用了其键的compare()方法,所以才能进行排序,但同时也规定了存入的key必须是同一类型对象。也可以调用其构造方法,在构造方法中提供 Comparator的实现类自定义排序策略。

Comparator和Comparable

Comparator

定制排序:重写compare(o1,o2)方法,需要传入两个对象进行比较;一般会先对比两个对象是否是同一类型,是同一类型再看按照哪个属性进行排序,然后分别获取两个对象的这个属性进行对比。

Comparable

内部排序,实现了此接口的类,表明该类支持内部排序;需要重写compareTo(o1)方法,通过该类的一个对象调用compareTo方法,传入该类类型的另一个对象按照指定的属性进行比较排序。这个比较排序是在该类内部自己进行判断的。

import java.util.Comparator;
import java.util.TreeMap;

public class Compare_test {
    public static void main(String[] args) {
        //按姓排序
        TreeMap treeMap = new TreeMap();
        Person person1 = new Person(18, "张三");
        Person person2 = new Person(20, "lisi ");
        Person person3 = new Person(10, "wangwu");
        Person person4 = new Person(1, "zhaoliu");
        treeMap.put(person1,3);
        treeMap.put(person2,2);
        treeMap.put(person3,1);
        treeMap.put(person4,0);
        System.out.println(treeMap);

        //按年龄排序
        TreeMap treeMap1 = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Person && o2 instanceof Person) {
                    Person p1 = (Person) o1;
                    Person p2 = (Person) o2;
                    return Integer.compare(p1.getAge(), p2.getAge());
                }
                throw new RuntimeException("输入类型不匹配");
            }
        });
        treeMap1.put(person1,3);
        treeMap1.put(person2,2);
        treeMap1.put(person3,1);
        treeMap1.put(person4,0);
        System.out.println(treeMap1);
    }
}

class Person implements Comparable{
    private int age;
    private String name;

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

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
    
    @Override
    public int compareTo(Object o) {
        if(o instanceof Person){
            Person person = (Person)o;
            return this.getName().compareTo(person.getName());
        }
        throw new RuntimeException("输入类型不匹配");
    }

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

线程安全的集合

vector、stack、HashTable、ConcurrentHashMap

将线程不安全的集合转换为安全的

  1. 集合类工具:Collections提供了synchornizedXxx()方法,将这些集合类包装成线程安全的集合类。所有的方法都带有同步锁(方法上都加了Synchornized)。
  2. JUC:JUC包下提供了大量支持搞笑并发访问的集合类,既能包装良好的性能,又能包装线程安全。(原理在多线程里面讲)
    1. 以Concurrent开头的集合类:写操作线程安全;读取不锁定。
    2. CopyOnWrite开头的集合类:没用锁。采用复制底层数组的方式进行写操作;读的时候读取集合本身;每个线程写的时候复制原数组到当前线程的一个新数组中,然后当前线程对这个副本进行操作。(每个线程拿一个副本数据写操作,写完刷到原数组中)很浪费内存。
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值