集合

1、集合的相关概念

1.1 集合框架

1、Java集合类由三个接口派生而出,Collection和Map和Iterator
在这里插入图片描述
2、Hash
指的是哈希码的一种算法、数据结构

1.2 相关区别
1.2.1 ArrayList和LinkedList的区别?

List常用的ArrayList和LinkedList?
1、区别:
ArrayList底层使用的是数组
LinkedList底层使用的是链表
数组:优点,查询数据速度快。缺点,插入删除修改比较慢(数组在内存中是一块连续的内存,如果插入删除需要移动内存)
链表:优点,插入删除元素效率高,缺点,查询需要从头部开始找,效率低

1.2.2 HashMap和HashTable的区别?HashMap和CurrentMap的区别?

1、HashMap和HashTable都可以存储key-value的数据
2、HashMap可以把null作为key或者value的,而HashTable是不可以的
3、HashMap是线程不安全的,效率高。HashTable是线程安全的,效率低。
4、CurrentHashMap既线程安全又效率高

2、根据底层代码实现集合

2.1 ArrayList
class MyLikedList{
    private Object[] date;
    private int size = 0;
    public MyLikedList(){
        date = new Object[10];
    }
    public void add(Object obj){
        if (size>=date.length) {
            Object[] dateMo = new Object[date.length*2];
            System.arraycopy(date,0,dateMo,0,size);
            date = dateMo;
        }
        date[size++] = obj;
    }
}

2.1 HashMap核心数据结构详解

底层实现
1、数组+链表+红黑树(链表与数组之间的平衡)
2、new 默认容量:16
3、红黑树的五个性质:
(1)每个节点要么是红的要么是黑的
(2)根节点是黑的
(3)每个叶节点是黑的
(4)如果一个节点是红的,那么他的两个儿子都是黑的
(5)对于任意节点而言,其到叶节点树尾端NIL指针的每条路径都包含相同数目的黑节点

3、集合框架概述

3.1集合

Java集合就像是一种容器,可以动态的把多个对象的引用放入容器中

3.2 数组在存储多个数据方面的特点:
  1. 一旦初始化之后,长度确定了
  2. 数组一旦定义好,元素的类型也就确定
  3. 数组中提供的方法很有限,对于添加、删除、插入操作不便
  4. 数组存储,有序可重复
  5. 数组中实际元素的个数没有现成的方法和属性
3.3 Java集合的分类
  1. collection:单列数据,定义了存取一组对象的方法的集合
    list:元素有序、可重复的集合
    Set:元素无序、不可重复的集合
    在这里插入图片描述
  2. Map接口:双列数据 ----> key value
    在这里插入图片描述

4、Collection

4.1 Collection中的API
  1. add()
  2. size()
  3. addAll():添加另外一个集合中的所有元素
  4. isEmpty():判断当前集合中是否有元素
  5. clear():清空集合元素
  6. contains()
  7. equals()
  8. containsAll()
  9. remove()
  10. retainAll():获取两个集合的交集
  11. hashCode():返回当前对象的哈希值
  12. toArray():集合转化为数组
  13. Array.asList(Arrays):数组转化为集合
  14. List item
  15. iterator():返回Iterator接口的实例,用于遍历集合元素
List list = Arrays.asList(new String[]{"12ew", "wqe" ,"dsad"});

注意:int类型数组不可以
要将int类型转化为Integer

4.2 集合元素的遍历
4.2.1 Iterator迭代器接口
  1. 概念:
    Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素
  2. 迭代器模式:
    提供一种方法访问一个容器对象中的各个元素,而又不暴露该对象的内部细节。迭代器模式,就是为容器而生。
  3. API
    (1) hasNext():判断是否还有下一个元素
    (2) next() :指针下移,将下移后集合位置上的元素返回
		Collection col = new ArrayList();
        col.add("213");
        col.add("2ewq13");
        col.add("21wwwwqe3");
        col.add("21rrrrewq3");
        Iterator iterator = col.iterator();
        System.out.println(iterator.hasNext());
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

(3)集合对象每次调用**Iterator()**方法都得到一个全新的迭代器 对象

(4)remove():可以在遍历过程中删除集合中的元素(注意:在未调用next()之前,不可以调用remove)

		Collection col = new ArrayList();
        col.add("213");
        col.add("2ewq13");
        col.add("21wwwwqe3");
        col.add("21rrrrewq3");
        Iterator iterator = col.iterator();
        System.out.println(iterator.hasNext());
        while (iterator.hasNext()) {
            Object object = iterator.next();
            if ("213".equals(object)) {
                iterator.remove();
            }
        
        iterator = col.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
4.2.2 froeach迭代集合和数组

实现也是iterator
在其中不可以修改值

4.3 List接口
4.3.1 List接口相关概念
  1. 鉴于Java数组用来存储数据的局限性,我们使用List代替数组(动态数组)
  2. 元素有序、可重复
  3. 相关实现类有:ArrayList、LinkedList、Vector
4.3.2 ArrayList、LinkedList、Vector的异同
  1. 相同点
    都实现了List接口,存储有序的、可重复的数据
  2. 不同点
    (1)Vector:list的古老实现类,JDK 1.0 。线程安全,效率低,底层使用Object[ ]存储
    (2)ArrayList:线程不安全,效率高,底层使用Object[ ]存储,查询较快
    (3)LinkedList:链表实现,底层使用双向链表存储,对于频繁的插入删除操作,使用此类的效率比ArrayList高(Collections中有一个方法为:synchronizedList返回的为线程安全的List)
4.3.3 ArrayList源码分析
  1. JDK 1.7中:
    (1)初始容量为10的Object[ ]数组
    (2)list.add(123);如果此次的添加导致底层的数组容量不够,则扩容为原来的1.5倍,同时将原来的数组中数据复制到新的数组中返回
    (3)建议开发中使用带参数的构造器
  2. JDK 1.8中:
    (1)底层初始化为容量为{ },并未直接创建长度
    (2)add()时,底层才创建了长度为10的数组
    (3)后续添加和扩容与JDK 1.7 无异
4.3.3 LinkedList源码分析
  1. LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,默认值为null
  2. list.add(112);将123封装到Node中,创建了Node对象
  3. Node对象为:
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
4.3.4 Vector源码分析
  1. 默认创建长度为10的数组
  2. 默认扩容为原来数组的两倍
4.3.5 List中的相关方法
  1. void add (int index, Object ele); // 在指定位置插入ele元素
  2. void addAll (int index, Collection coll);
  3. Object get(int index);
  4. int indexOf(Object obj);
  5. int lastIndexOf(Object obj);
  6. Object remove(int index );
  7. Object set(int index, Object ele);
  8. List subList(int from index, int toIndex);
4.3.6 List中的遍历
  1. Iterator 迭代器方式
  2. 增强for循环
  3. 普通循环
4.4 Set接口
4.4.1 相关概念:
  1. 相关实现类:HashSet、LinkedHashSet、TreeSet
  2. set接口存储无序的不可重复的数据
    (1)无序性:
    不等于随机性,遍历时有所谓的顺序
    存储的数据,在底层的数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的
    (2)不可重复性:
    保证添加的元素按equals判断时,不能返回true。即,相同的元素只可以添加一个
4.4.2 HashSet、LinkedHashSet、TreeSet异同
  1. HashSet:作为Set接口的主要实现类,线程不安全的;可以存储null值
  2. HashSet ----> LinkedHashSet:作为HashSet子类实现的,遍历其内部数据时,可以按照添加的顺序遍历
  3. TreeSet:可以按照添加对象的指定属性,进行排序
4.4.3 set中添加元素的过程(以Hashset为例):
  1. 我们向HashSet中添加元素,调用HashCode方法计算哈希值,此哈希值经过某种算法算出Hashset在底层数组中的存放位置(索引位置),然后判断此位置上是否已经有元素
  2. 如果此位置上没有其他元素,则直接添加该元素 ---- 情况1
  3. 如果此位置上有其他元素,则比较两个元素的哈希值
    (1)如果不同,则元素a添加成功 ---- 情况2
    (2)如果相同,进而需要调用equals比较两个元素是否相等
    返回true,则该元素添加失败
    如果返回false,则该元素添加成功 ---- 情况3
  4. 注意:对于添加成功的情况 2情况3而言,元素和已经在指定索引位置上的数据以链表的方式存储
    (1)JDK 1.7中:该元素放到数组中,指向原来的元素
    (2)JDK 1.8中:原来的元素在数组中,指向新元素
  5. 向set中添加的数据,其所在的类一定要重写hashCode()和euqlas方法,重写的hashCode()和euqlas方法尽可能保持一致性
4.4.4 IDEA中的hashCode()重写:

数字31

    public static int hashCode(Object a[]) {
        if (a == null)
            return 0;
        int result = 1;
        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());
        return result;
    }
  1. 选择系数时要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突越小”,查询起来的效率越高(减少冲突)
  2. 31只占用5bits,相乘造成的数据溢出的概率较小
  3. 31 可以 由 i*31 == (i << 5) -1 来表示,现在很多虚拟机里面都有相关的优化(提高算法效率)
  4. 31是一个素数,素数的作用就是如果我用一个数字来乘这个素数,那么最终出来的结果只能被素数本身和被乘数还有1来整除!(减少冲突)
4.4.5 HashSet的底层实现
	public HashSet() {
        map = new HashMap<>();
    }
4.4.6 LinkedHashSet的使用
  1. 添加的时候无序
  2. 在添加数据时,每个数据还维护了两个引用,记录前一个数据和后一个数据
  3. 优点:对于频繁的遍历,效率要高于HashSet
4.4.6 TreeSet-自然排序
  1. 只能添加同一类型的数据
  2. 按照添加数据类型排序
  3. 两种排序方式
    (1)自然排序(实现Comparable接口):比较两个对象是否相同的标准为conpareTo(),返回0,不再是equals()
public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        treeSet.add(new User("li", 23));
        treeSet.add(new User("lisa", 2213));
        treeSet.add(new User("lisa", 233));
        treeSet.add(new User("li", 213));
        treeSet.add(new User("jie", 23));
        System.out.println(treeSet);
    }
}
class User implements Comparable{
    String name;
    int age;
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Object o){
        if (o instanceof User) {
            User user = (User)o;
            int compare =  this.name.compareTo(user.name);
            if(compare !=0) {
                return compare;
            } else {
                return Integer.compare(this.age, user.age);
            }
        } else {
            throw new RuntimeException("exception ------ ");
        }
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

(2)定制排序(Comparator):重写compare()方法

public class TreeSetTest {
    public static void main(String[] args) {
        Comparator comparator = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User && o2 instanceof User) {
                    User user1 = (User) o1;
                    User user2 = (User) o2;
                    int flag = user1.name.compareTo(user2.name);
                    if(flag !=0) {
                        return flag;
                    } else {
                        return Integer.compare(user1.age, user2.age);
                    }
                } else {
                    throw new RuntimeException("exception ------ ");
                }
            }
        };
        TreeSet treeSet = new TreeSet(comparator);
        treeSet.add(new User("li", 23));
        treeSet.add(new User("lisa", 2213));
        treeSet.add(new User("lisa", 233));
        treeSet.add(new User("li", 213));
        treeSet.add(new User("jie", 23));
        System.out.println(treeSet);
    }
}
4.4.7 面试题
  1. 例1:
public class HashTest {
    public static void main(String[] args) {
        Person p1 = new Person(11, "李思");
        Person p2 = new Person(12,"李大");
        HashSet hashSet = new HashSet();
        hashSet.add(p1);
        hashSet.add(p2);
        System.out.println(hashSet);
        p1.name = "礼物";
        hashSet.remove(p1);
        System.out.println(hashSet);
        hashSet.add(new Person(11, "李思"));
        System.out.println(hashSet);
    }
}
class Person{
    int num;
    String name;
    public Person() {
    }
    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        if (num != person.num) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }
    @Override
    public int hashCode() {
        int result = num;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", name='" + name + '\'' +
                '}';
    }
}

输出结果:

[Person{num=12, name='李大'}, Person{num=11, name='李思'}]
[Person{num=12, name='李大'}, Person{num=11, name='礼物'}]
[Person{num=12, name='李大'}, Person{num=11, name='礼物'}, Person{num=11, name='李思'}]

分析:哈希值不同,返回同一个对象

5、Map

5.1 基本概念
  1. 双列数据,存储key-value对的数据
  2. HashMap :作为Map的主要实现类;线程不安全,效率高,可存储null的key和value(注:)
    -----> LinkedHashMap:保证在遍历元素时,可以按照添加的顺序实现,对于频繁的遍历操作等使用
  3. Hashtable:古老的实现类,线程安全,效率低,不能存储null和value的key和value
    -----> Properties:常用来处理配置文件,key和value都是String类型
  4. TreeMap:保证按照添加的key-value进行排序,实现排序遍历,此时考虑KEY的自然排序或者定制排序
  5. 对于key-value的理解:
    (1)value可以重复,key可重复且无序
    (2)使用set存储所有的key
    (3)key所在的类要重写equals()和hashCode()
5.2 HashMap底层实现

在这里插入图片描述

  1. HashMap的底层:
    数组+链表(JDK 1.7)
    数组+链表+红黑树(JDK 1.8)

  2. JDK 7
    (1)HashMap hashMap = new HashMap(); // 底层创建了一个长度是16的一维数组Entry[ ] table
    (2)经过hashCode()计算Entry在数组中存放的位置,如果此位置上的数据为空,直接存(超出容量大小时,扩容为两倍)
    如果此位置上的数据不为空,(以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:如果key1的哈希值与已经存在的数据的哈希值都不相同,此时entry添加成功。如果key1的哈希值和已经存在的某个数据的哈希值相同,使用equals方法比较数据。
    (3)当超出临界值(且要存放的位置非空)时,扩容为原来容量的二倍

  3. JDK 8
    (1)new HashMap()没有创建固定长度的数组
    (2)底层的数组为Node[ ],非entry[ ]
    (3)首次调用put()方法时,底层创建长度为16的数组
    (4)当某一个索引位置上的元素以链表的形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所有数据改为使用红黑树存储

DEFAULT_LOAD_FACTOR; // HashMap默认加载因子 0.75
threshold; //12 容量*填充因子  16 * 0.75
为什么是 0.15 既兼顾到数组的利用率,又尽可能的让链表的数量少一些
5.3 LinkedHashMap底层的实现原理
  1. 可以按照添加的顺序遍历
  2. LinkedHashMap中的内部类:Entry[ ] entry;
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }
5.4 Map的使用:
5.4.1 Map中定义的方法:
  1. put(); // 添加或者修改
  2. putAll();
  3. Object remove(Object key);
  4. void clear();
  5. boolean containsKey(Object key);
  6. boolean containsValue(Object value);
  7. boolean equals(Object obj);
5.4.2 Map的遍历
  1. Set keySet(); 返回所有的key构成的Set集合
  2. Collection values();
  3. Set entrySet = map.entrySet(); 遍历所有的key-value
5.5 TreeMap解析
  1. 向TreeMap中添加key-value,要求key必须是由同一种类型的数据
  2. 要按照key进行排序:自然排序、定制排序
5.6 Properties解析
5.6.1 基本概念
  1. Properties类是Hashtable的子类,该对象用于处理属性文件
  2. 属性文件中的key-value都是字符串,所以Properties里的key和value都是字符串类型
  3. 存取数据时,使用setProperties(String key, String value)和getProperty(String key)方法
5.6.2 从配置文件读取数据
public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        FileInputStream fies = new FileInputStream("jdbc.properties");
        properties.load(fies); // 加载流对应的文件
        System.out.println(properties.getProperty("name"));
    }

6、Collections工具类

6.1 基本概念
  1. 是一个操作Set、List、Map等集合的工具类
  2. 提供了一系列静态的方法对集合元素进行排序、查询和修改等操作。
  3. 提供了对于集合元素对象设置不可变、对集合对象实现同步控制等方法
6.2 基本方法:
  1. reverse(List);
  2. shuffle(List); // 对List集合元素随机排序
  3. sort(List); // 根据元素的自然排序对指定的List元素按升序排序
  4. sort(List, Comparator);
  5. swap(List, int, int); // 交换数据
  6. Object max/min(Collection);
  7. Object max/min(Collection, Comparator);
  8. int frequency(Collection, Object); //返回指定集合中的元素出现的频率
  9. Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程鬓发访问集合时的安全问题
        List list1 = Collections.synchronizedList(list);
6.3 Collection和Collections的区别
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值