JAVA——集合理解

集合复习

一.集合就像一种容器,可以动态的把多个对象的引用

放入到容器中,存储多个数据 。

集合数组都是对多个数据进行存储操作的结构,简称java容器

二、集合框架、

collection接口:单列集合,用来存储一个一个对象

​ list接口:存储有序可重复的数据

​ 实现类:ArrayList LinkedList

​ set接口:存储无序,不可重复的数据

​ 实现类:HashSet

map接口:双列集合,用来存储一对一对的数据

​ 实现类:HashMap

三、collection(为什么不用collection? 因为即有序又无序,即可重复,又不可重复,找不到这样的容器,所以才会用List和Set)

collection的方法:(需要重写元素所在类的equals方法)

  1. add() //添加元素,可以为简单类型,和引用类型 ,返回boolean

    Collection coll=new ArrayList();
           coll.add("aa");
  2. addAll(Collection c)//添加集合数据
    Collection coll=new ArrayList();
    Collection coll1=new ArrayList();
    coll1.add("bb");
    coll.addAll(coll1);
  3. clear() //清空集合
    Collection coll=new ArrayList();
    coll.add("aa");
    coll.clear();
  4. isEmpty() // 判断是否为空,没有元素
    Collection coll=new ArrayList();
    coll.add("aa");
    coll.clear();
    coll.isEmpty();
  5. contains(Object obj)// 是否包含一个对象,判断的是其内容,内容一致则结果为true,内容不一致则结果为false,这里对于String类型,其equals方法已经重写了,故而只需要判断内容是否一致即可,但是对于JAVA BEAN类型,需要重写其equals方法,否则会判断为false。 可看代码后的总结

    例如:

    private String name;
    private int age;
    public Person() {
       }
    
       public Person(String name, int age) {
           this.name = name;
           this.age = age;
       }
    
       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;
       }
    
       @Override
       public String toString() {
           return "Person{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
       }
    //重写equals方法,如果不写,则结果为false
       @Override
       public boolean equals(Object o) {
           System.out.println("kaishi l ");
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
           Person person = (Person) o;
           return age == person.age &&
                   Objects.equals(name, person.name);
       }
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.Date;
    
    public class Demo {
       public static void main(String[] args) {
           Collection coll=new ArrayList();
           coll.add("aa");
           coll.add("bb");
           System.out.println(coll.contains("aa"));//true ,判断是否有aa,有,则为true
    
           coll.add(new String ("huhu"));
           System.out.println(coll.contains(new String("huhu")));//结果为true,因为string底层已经把equals重写过了,直接判断内容
    
           coll.add(new Person("wwuu",123));
           System.out.println(coll.contains(new Person("wwuu",123)));//因为上面已经对Person对象进行重写了equals方法,这里直接判断是否有wuwu,123.有则为true
           //如果上面没有对Person对象的equals方法进行重写,那么这里为false
           //总结:contains比较的是内容,等于用equals方法进行比较
           //我们在判断时,会调用obj对象所在类的equals()方法,故而obj所在类要重写equals()方法。
       }
    }
    
  6. containsAll(Collection coll1)//判断coll集合中的所有元素是否都存在于当前集合中(注意是:所有元素,但凡有一个不在,则为false)
    Collection coll=new ArrayList();
    Collection coll1=new ArrayList();
    coll.add("123");
    coll1.add("haha");
    coll.addAll(coll1);
    System.out.println(coll.containsAll(coll1));//true,上面用了addAll()方法,把coll1中的数据都放到了coll中,所以coll包含了coll1,为true
    coll1.add("uiui");
    System.out.println(coll.containsAll(coll1));//false,这里是因为coll1新加了一条数据,而coll中没有这条数据,故而为false
  7. remove(Object obj) (删除某个元素,重写元素所在类的equal()方法)
    coll.remove(new Person("wwuu",123));
    System.out.println(coll);//由于上述的方法中已经对Person这个类进行了equals()方法的重写,故而这里是可以对new Person("wwuu",123)这个元素进行删除的,如果不重写equals方法则删除不了
  8. remoeAll(Collection coll)(删除coll中所有的元素,重写元素所在类的equal()方法)
    coll.removeAll(coll1);
    System.out.println(coll1);
    System.out.println(coll);
  9. retainAll(Collection coll)(求交集的意思,仅仅保留当前集合中的元素并且该元素同时也在另一个集合中,即获取当前集合与coll的交集,重写元素所在类的equal()方法)
    
    Collection coll2=new ArrayList();
           coll2.add(123);
           coll2.add(new String ("huhu"));
           coll2.add(new String("bb"));
           coll.retainAll(coll2);
           System.out.println(coll);
  10. equals(Object o) (这里是判断两个集合是否相等,那么这里的obj必须是集合才行,也要重写元素所在类的equals()方法。对于有序集合,其位置也不必须相同,若为无序集合,其位置可以不同,只需要元素相等即可。)
    Collection coll3= Arrays.asList(123,"huhu","bb");
    System.out.println(coll2.equals(coll3));
  11. hashCode() (计算机hashCode哈希值的方法,返回值是int)
    System.out.println(coll3.hashCode());
  12. toArray() (集合转数组)
    Object[] objects = coll3.toArray();
            for (Object o: objects) {
                System.out.println(o);
            }
    拓展一下:数组转集合(使用的时候注意 Arrays.asList(T)的参数是可变形参,根据不同需要看是写int 还是其包装类integer ,int会判断成为一个参数,integer为两个参数)
    List list2 = Arrays.asList(new int[]{123, 455});
    List list3 = Arrays.asList(new Integer[]{123, 455});
    System.out.println(list2.size());//结果是1,判断一个参数
    System.out.println(list3.size());//结果是2,判断2个参数
  13. iterator()(迭代器接口:遍历集合的时候使用)
    Iterator iterator=coll2.iterator();
    while (iterator.hasNext()){
           System.out.println(iterator.next());
    }
    拓展:内部定义了一个remove方法,可以删除集合中的元素
  14. foreach
    foreach(Object obj: coll2){
        System.out.println(obj);
    }

LIst----有序可重复(俗称:动态数组)

他是一个接口,通常用List代替数组,有三个实现类ArrayList、LinkedList、Vector

三者的异同:ArrayList、LinkedList、Vector?

相同点:三个类都实现了List接口;存储数据特点相同,有序,可重复

不同点:

​ ArrayList:作为List接口的主要实现类,基本都用他,线程不安全,执行效率高,查询快;底层使用的是Object[ ] elementData数组储存数据

​ LikedList:对于频繁的插入和删除操作,使用LinkedList比ArrayList效率高,底层使用的是双向链表储存

​ Vector:List接口的古老实现类,很少用,JDK 1.0就有了,其他的都是1.2出现的,包括List,线程安全,执行效率低;底层是使用Object[ ] elementData储存数据

ArrayList源码分析(java.util包下):

JDK7:

​ JDK7在造对象的时候就给把底层的数组进行了初始化

ArrayList arraylist=new ArrayList();

1566870572500

1566870822848

JDK8:并没有给原始数组初始化,节省内存空间,这样的方式更好,在我们调用add方法的时候,我们的数组才会被创建好

1566871345927

1566871300000

总结:

1566871955045

LinkedList源码分析

​ 是一个双向链表,


import java.util.LinkedList;

public class LinkedListTest<E> {
    int size;
    transient MyNode first;
    transient MyNode last;

    private static class MyNode<E>{
        private E element;
        private MyNode prev;
        private MyNode next;
        public MyNode(MyNode prev, E ele, MyNode next){
            this.element=ele;
            this.prev=prev;
            this.next=next;
        }
    }
//添加元素的方法,里面有一个拼接的方法一个Node分为三部分:【prev】【元素】【next】
    public void add(E ele){
        appendLast(ele);
    }
    //定义l节点为last,
    public void appendLast(E ele){
        MyNode<E> l=last;
        MyNode newNode=new MyNode(l,ele,null);
        //last节点为新节点
        last=newNode;
        //判断新节点的【prev】是否为null,是则表示为该新节点为【null】【ele】【null】
        //表示新结点为first
        //否则,表示不是首节点,则把last的【next】指向新结点
        if(l==null){
            first=newNode;
        }else {
            l.next=newNode;
        }
        size++;
    }

    public void remove(E obj){
        //删除元素
        //从头开始遍历,一直遍历到结尾,只要没到结尾就继续遍历
        for (MyNode pos=first;pos!=null;pos=pos.next){
            //判断,当前元素pos是不是想要删除的元素,是的就进行unlink方法,只要找到了就break
            if (pos.element.equals(obj)){
                unlink(pos);
                break;
            }
        }
    }
    //这里就是删除元素的方法
    private void unlink(MyNode<E> pos){
        //首先定义当前pos元素【prev】【ele】【next】
        E ele=pos.element;
        MyNode<E> prev=pos.prev;
        MyNode<E> next=pos.next;
        //判断当前元素的【prev】是不是空,如果是空,那就表示是first,为第一个节点
        //那就直接把第一个删除,然后把pos后面的节点MyNode<E> next=pos.next 赋值给first
        //并把当前元素pos的【next】变成null,就不会指向后面的结点了,就断开连接了
        if (prev==null){
            first=next;
            pos.next=null;
        }else {
            prev.next=next;
            pos.prev=null;
        }
        //判断当前元素是不是最后一个,如果是就删除最后一个元素,即让前一个元素的变成为最后一个last结点
        //否则把下一个元素的【prev】指向prev结点
        if (next==null){
            last=prev;
            pos.prev=null;
        }else {
            next.prev=prev;
            pos.next=null;
        }
        pos.element=null;
        size--;
    }
}

Set集合

是接口,存储无序不重复的数据,Set接口继承了Collection接口,同时没有添加新的方法,所以在使用上与List接口的实现类使用方式一致。都是有增加元素的add方法和获取元素个数的size方法

要求:
向set中添加数据,其所在类一定要重写hashCode()和equals()方法;
重写的hashCode()和equals()尽可能地保持一致:即相同对象必须拥有相同散列码(相同对象要有一样的哈希值)

具有三个实现类:

HashSet:作为set接口的主要实现类,线程不安全:可以存储null值

LinkedHashSet:是Hashset的子类,在Hashset基础之上,添加了指针,遍历其内部子类的时候,可以按照添加的顺序遍历。

TreeSet:使用红黑树存储,要求放在TreeSet中的数据必须是同类型,可以按照添加对象的知道属性进行排序

无序不可重复的理解:

1.无序性:解释HashSet集合如何存放元素:

​ 无序性:不等于随机性。存储的数据在数组中并非按照数组索引的顺序进行添加。而是根据数据的hash值决定的。

​ 当我们给HashSet中存放元素的时候,这时并不是直接把这个元素就存放到HashSet内部维护的数组中。

​ 而是先根据当前要存放的那个元素,再结合一种算法(这个方法就是Object类中的hashCode方法),算出当前这个元素应该在数组中存储的位置。

​ 在存放数据的时候,如果计算出来的位置上已经有元素,这时还会去调用当前正要存放的这个元素的equals方法,把已经在计算出位置上的那个元素一起进行比较,如果equals方法返回的true,就丢弃当前正要存放的元素。如果equals方法返会的false,当前这个对象还要存储。

2.不可重复性:HashSet集合是如何保证元素不重复

​ 不可重复性:表示在hashset中的元素 是不可重复的

当给hashset中存放元素的时候会先调用对象的hashCode方法,计算哈希值,根据哈希值来决定当前对象在集合中的存储位置。

​ 在存储的时候,如果遇到了哈希值相同的元素,这时集合的底层还会去调用当前对象的equals方法,判断当前正要存放的对象和

​ 位置上已经存在的对象是否是同一个对象,equals方法返回的true,就认为相同对象,不保存,如果equals方法返回的false,当前对象已经会被保存

1566963564305

计算哈希值得时候减少冲突

1566964307805

1566964075136

链表与数组的区别:

  1. 对于在内存中数据的存放顺序及位置:

    数组中元素是按顺序存放的,那么在内存中也是按顺序存放的,但是链表中元素存放的位置与内存中存放的顺序及位置不一致;

    2.对于扩容的速度:

​ 链表的速度要快于数组,数据的扩容需要在内存在新申请一片内存空间,而链表直接扩就行

Map接口

存储双列数据:存储key--value键值对的数据

一、实现类

HashMap:主要实现类;线程不安全,效率高,可以存储null的key和value,key为null可以使用方法,不会报空指针异常
LinkedHashMap---HashMap的子类;保证在遍历map元素时,可以按照添加的顺序实现遍历。

​ 原因:在原有的hashMap底层结构基础上,添加一对指针,指向前一个和后一个元素。

​ 对于频繁的遍历操作, 此类执行效率高于HashMap

TreeMap:可以按照添加的key-value进行排序,实现遍历,按照key进行排序

​ 底层使用红黑树进行排序

Hashtable----是古老实现类;线程安全,效率低,不能存储null的key和value,key为null可以使用方法,会报空指针异常
properties---Hashtable的子类,常用来处理配置文件,key和value嗾使String类型
线程安全

1566965278032

面试题:

1.(高频)HashMap底层实现原理

2.HashMap和Hashtable的异同

二、MAP结构的理解:

​ Map中 的key:无序的、不可重复,使用set存储所有的key,

​ 要求:key所在的类要重写equals()和hashCode()

​ Map中的value:无序的、可重复的,使用Collection存储所有的value

​ 要求:value所在的类要重写equals()方法

​ 一个键值对:key-value构成了一个Entry,key、value就是entry的属性

​ Map中的entry:无序的,不可重复的,使用set存储所有的entry

三、HashMap的底层原理

哈希冲突:

解决方法1:再哈希法===再次计算哈希值,直到每一个元素都有唯一的索引(位置)为止

解决方法2:链地址法===使用链表

JDK7

1567015171057

import java.util.HashMap;

public class HashMapTest <K,V>{

    private Entry<K,V>[]table;
    private static final Integer INITCAPACITY=8;
    private int size;
    public HashMapTest(){
        table=new Entry[INITCAPACITY];
    }
    private int size(){
        return size;
    }
    private V get(Object key){
        int hash=key.hashCode();
        int i=hash%table.length;
        for (Entry<K,V> entry=table[i];entry!=null;entry=entry.next){
            if (entry.k.equals(key)){
                return entry.v;
            }
        }
        return null;
    }
    private V put(K key,V value){

        int hash=key.hashCode();
        int i=hash%table.length;
        for (Entry<K,V> entry=table[i];entry!=null;entry=entry.next){
            if (entry.k.equals(key)){
                V oldvalue=entry.v;
                entry.v=value;
                return oldvalue;
            }
        }
        addEntry(key, value, i);
        return null;
    }

    private void addEntry(K key, V value, int i) {
        Entry<K,V> entry=new Entry<K,V>(key,value,table[i]);
        table[i]=entry;
        size++;
    }

    class Entry<K,V>{
        private K k;
        private V v;
        private Entry<K,V>next;

        public Entry() {
        }

        public Entry(K k, V v, Entry<K, V> next) {
            this.k = k;
            this.v = v;
            this.next = next;
        }

        public K getK() {
            return k;
        }

        public V getV() {
            return v;
        }

    }

    public static void main(String[] args) {
        HashMapTest<Integer ,String>hashMapTest=new HashMapTest<Integer, String>();
        for(int i=0;i<10;i++){
            hashMapTest.put(i,"结束"+i);
            System.out.println(hashMapTest.get(i));
        }

    }
}

1567015212088

猜你喜欢

转载自blog.51cto.com/14522074/2434382