Java集合知识点

1.常见的集合

Collection接口和Map接口是所有集合的父接口;Collection接口的子接口有List接口和Set接口

Collection接口:

        List接口:ArrayList,LinkedList,Stack,Vector

        Set接口:HashSet,TreeSet,LinkedHashSet

         Map接口:HashMap,TreeMap,HashTable,LinkedHashMap,ConcurrentHashMap,Properties(常用于读取配置文件)

2.常见集合的底层实现的结构

Collection类型集合:

List接口:有序,元素可重复,支持下标和迭代器遍历元素

  • ArrayList:数组实现,非线程安全
  • LinkedList:双向链表实现,非线程安全
  • Stack:数组实现,先进后出FILO,线程安全
  • Vector:数组实现,先进先出FIFO,线程安全

Set接口:大部分无序,部分有序,仅支持迭代器遍历元素,加入Set集合的对象需要重写equals()方法

  • HashSet:HashMap实现,非线程安全
  • TreeSet:TreeMap实现,非线程安全,有序
  • LinkedHashSet:LinkedHashMap实现,非线程安全

Map类型集合:部分有序,key-value类型的集合

  • HashMap:数组+链表实现,jdk1.8链表长度大于8时链表转为红黑树,非线程安全
  • HashTable:数组+链表实现,线程安全
  • TreeMap:红黑树实现,有序,非线程安全
  • LinkedHashMap:HashMap实现,有序,非线程安全
  • ConcurrentHashMap:数组+链表实现,线程安全
  • Properties:线程安全

3.HashMap与HashTable的区别

HashMap:非线程安全,效率高,key和value可以为null; 

HashTable:线程安全,底层相关函数使用synchronized同步锁修饰,效率低于HashMap,key与value都不能为null。

  4.集合排序实现方式

  • comparable方式:重写compareTo方法,也叫自然排序
  • comparator方式:重写compare方法,按需编写排序规则的类,因此也叫定制排序
import java.util.*;

/**
 * @Description
 * @Author YMJ
 * @DateTime 2020-07-26 20:46
 * @Version V1.0.0
 */
public class CollectionSort {
    public static class Student implements Comparable<Student>{
        String username;
        int age;

        public Student(String username, int age) {
            this.username = username;
            this.age = age;
        }

        @Override
        public int compareTo(Student o) {
            if (this.username.compareTo(o.username)<0){//按名字降序排列
                return 1;
            }
            if (this.age > o.age){//按年龄从大到小排序
                return -1;
            }
            return 0;
        }


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

    public static class CollectionSortRule implements Comparator<Student>{

        @Override
        public int compare(Student o1, Student o2) {
            if(o1.username.compareTo(o2.username)<0){//按名字自然排序
                return -1;
            }
            if((o1.username.compareTo(o2.username) == 0) && (o1.age < o2.age)){//按年龄从小到大排序
                return -1;
            }
            return 0;
        }
    }

    public static void main(String[] args) {
        List<Student> list = new ArrayList();
        Student s1 = new Student("yu",22);
        Student s2 = new Student("ming",23);
        Student s3 = new Student("ju",21);
        Student s4 = new Student("ju",26);
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);

        System.out.println("使用Comparable进行排序:名字降序,年龄降序");
        Collections.sort(list);
        list.forEach(System.out::println);

        System.out.println("使用Comparator自定义排序类进行排序:名字升序,年龄升序");
        list.sort(new CollectionSortRule());
        list.forEach(System.out::println);

        System.out.println("使用Comparator自定义匿名内部排序类进行排序:名字降序,年龄降序");
        list.sort(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                if (o1.username.compareTo(o2.username) > 0) {//按名字降序排列
                    return -1;
                }
                if (o1.username.compareTo(o2.username) == 0) {//按年龄从大到小排序
                    return o2.age - o1.age;
                }
                System.out.println(o2.username.compareTo(o1.username));

                return 0;
            }
        });
        list.forEach(System.out::println);

    }
}
使用Comparable进行排序:名字降序,年龄降序
Student{username='yu', age=22}
Student{username='ming', age=23}
Student{username='ju', age=26}
Student{username='ju', age=21}
使用Comparator自定义排序类进行排序:名字升序,年龄升序
Student{username='ju', age=21}
Student{username='ju', age=26}
Student{username='ming', age=23}
Student{username='yu', age=22}
使用Comparator自定义匿名内部排序类进行排序:名字降序,年龄降序
Student{username='yu', age=22}
Student{username='ming', age=23}
Student{username='ju', age=26}
Student{username='ju', age=21}

 

5.HashMap(jdk1.7)知识点

    hashmap是基于hash函数来存取数据的,hashmap存储的是键值对Entry(key-value);其数据结构是数组加链表的形式。

  • put的工作原理

put(key,value)时,首先对key对象进行hash计算得到器hashcode(即散列码),然后通过indexfor方法让hashcode与(数组长度lenght-1)相与&得到bucket位置。如果此位置上没有元素,那么就直接插入这个键值对;如果有元素(hash冲突),那么就去调用当前位置上键值对的key对象与待插入键值对的key对象做equal比较,如果键对象相同则比较其值对象,如果值不同则在此位置插入值以更新其值,如果值相同则不做任何操作;如果此位置上的键对象与待插入键值对的键对象不同,则它会插入到此位置链表的头结点上。

  • get的工作原理

get(key)时计算key对象的hashcode并通过indexfor计算器bucket位置,找到后遍历此位置链表上的所有节点上的键值对,将节点上的key对象与指定的key对象进行equal比较,直到找到相等的后返回对应的值对象。

  • hashmap的扩容原理

默认的hashmap初始数组大小为16,其负载因子为0.75,也就是在一般情况下元素个数到达16*0.75=12时就会扩容,它是重新生成一个数组,大小为原来数组的2倍,(在jdk1.8中链表节点长度大于8时会生成红黑树),这一步叫做resize;然后对原来数组中的对象再一次进行hash计算,这一步叫做rehash,然后正在进行indexfor计算出bucket位置,将此元素插入到新数组当中

  • 在多线程中重新调整hashmap大小引发的问题

会产生条件竞争,在hashmap重新调整大小时链表表中的数据顺序会倒转过来,因为hashmap每次往链表插入数据插入的是头部(jdk1.8及之后则是采用尾插法),这是为了防止尾部遍历,条件竞争会产生死循环,因为hashmap本来就不是线程安全的,所以不推荐在多线程下使用hashmap,可以选择使用concurrenthashmap或则hashTable

6.concurrentHashMap(jdk1.7)知识点

    concurrentHashMap1.7采用数据结构是segment数组,每个segment的数据结构是hashEntry数据,hashEntry的数据结构是链表,segment继承ReentrantLock,所以在写入时需要获取锁进行操作。(分段锁+数组+链表)

  • GET操作

       key通过两次hash计算在segment数组的位置,在进行一次hash计算出在hashEntry数组中的位置,后面的操作和hashmap一致。整个get过程不需要加锁,除非读到的值为空才会加锁重读(可能某个线程删除节点导致),使用volatile多线程读取的正确性。

  • PUT操作

       key通过两次hash计算在segment数组的位置,在进行一次hash计算出在hashEntry数组中的位置,比对过程和hashMap一致,在插入前会先获取锁,首先判断当前这个segment的hashEntry数组是否需要扩容,不需要则进行插入操作,需要的话则进行扩容后在插入(只会扩容当前所在的segment里的hashEntry数组)。

  • 删除操作

        删除节点的前面所有节点复制一遍然后用头插法插入链表,原因是每个节点的next是final的不可修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值