java集合


前言

集合就是存储数据的,和数组一样,但是数组存在一些弊端,
->数据是内存中连续的空间,存储的数据有限,
->数组大小一旦确定无法修改
->有序可重复,对于无需不可重复没有办法
->没有提供直接操作集合中元素的方法。

一、Collection(单列集合)

1、UML图

在这里插入图片描述

2、List接口常用方法

3、list接口(有序可重复)

①、有序可重复,在Collection的基础上加了可以通过下标操作集合中的元素(插入,删除,获取(元素get,下标indexOf),修改)
②、读取效率高,插入删除效率低

1)ArrayList(变长数组,非线程安全)

1、内部使用数组存储,读取效率高,插入删除效率低,
2、源码:
①JDK1.7
构造方法:无参构造方法初始化容量为10,
添加元素方法:当容量不够时,会进行数组扩容,扩容的大小是原来的1.5倍(原来数组大小左移一位加上原来数组大小),然后将原来数组内容copy到新的数组中
在这里插入图片描述
②JDK1.8
里面定义了一个默认的空数组的常量,初始化时不会创建初始化数组大小,而是设置为{},当第一次添加时,通过数据扩容的方式初始化数组容量大小为10和上图一样的方法

在这里插入图片描述

/**
 * list:有序可重复,在自身中又将父类方法定义了一遍???
 * 理解:变长数组,1.8默认是0,插入元素时扩容10,1.7是默认10大小
 *       public ArrayList() {
 *           this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//{}
 *       }
 *
 * list方法:新增了根据下标操作的集合
 *  void add(int,Object):指定位置插入元素,<=size,插入
 *  Object get(int):指定下标获取元素,<size
 *  int indexOf(Object):获取指定元素的第一个的下标,
 *  int lastIndexOf(Object):获取该元素的最后一个的下标
 *  Object remove(int):删除指定下标元素,<size
 *  Object set(int,Object):设置指定下标元素为obj
 *  List subList(int,int):返回指定下标到指定下标的元素,不包括最后一个下标
 *
 *  ArrayList测试list方法
 *  ArrayList:数组存储,读写很快,插入删除
 *
 *
 */
public class MyArrayList {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(12);
        list.add(0,list.size());//不能超出size大小
        list.add(0,0);//不能超出size大小
        System.out.println(list);
        list.set(0,Integer.MAX_VALUE);
        System.out.println(list.remove(2));
        System.out.println(list.subList(0,2));//不包括最后一个下标
        System.out.println(list);
    }
}

2)LindedList(双向链表,非线程安全)

1、双向链表,内有头节点和尾节点(节点存储节点信息和前后节点)
2、节点操作插入删除效率高,读取效率低
3、新增节点的操作方法
4、源码:
内部有头部节点和尾部节点,
构造方法:初始化容量大小为0
add方法:尾插法,第一次插入时,头节点和尾节点相同

   void linkLast(E e) {
        LinkedList.Node<E> l = this.last;
        LinkedList.Node<E> newNode = new LinkedList.Node(l, e, (LinkedList.Node)null);
        this.last = newNode;
        if (l == null) {
            this.first = newNode;
        } else {
            l.next = newNode;
        }

        ++this.size;
        ++this.modCount;
    }

在这里插入图片描述

/**
 * 实现类LinkedList:单列集合,有序可重复
 *  理解:插入删除很快,双向链表(内部有头结点和尾节点),读写慢
 *  常用方法:(本类新增),只有一个时,头尾节点是一样的。
 *      void addFirst(Object)头插法
 *      void addLast(Object)尾插法
 *      Object getFirst()
 *      Object getLast()
 *      Object removeFirst()
 *      Object removeLast()
 *
 *
 *
 */
 Collection list=new LinkedList();//接口的上转型。也可以指定类型插入,也可以插入自定义类型,但是比较方法需要重写。
        String str="qwer";//直接指向常量池的
        //增
        System.out.println("插入元素");
        list.add("fds");
        list.add(new Integer(3));
        list.add(new String("fds"));
        list.add(34);//自动装箱。
        list.add(str);

        //查(for)需要转型,才能获取到正确的数据类型。
        System.out.println("当前数组中含有的元素:");
        for(Object obj:list){
            System.out.println(obj);
        }

        //删
        System.out.printf("删除元素'fds':");
        boolean success=list.remove(new String("fds"));//调用String的equals方法
        System.out.println((success)?"删除成功":"删除失败");

        //查(迭代)
        System.out.println("当前数组中含有的元素:");
        Iterator it=list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        //判断是否为空
        boolean empty=list.isEmpty();
        System.out.println(empty?"数组为空":"数组不为空");
        /*list.clear();//清空数组
        System.out.println("清空数组");
        empty = list.isEmpty();
        System.out.println(empty?"数组为空":"数组不为空");*/

        //是否包含某个元素,调用的是String的equals方法;//比较的是这个变量的值啊。
        boolean contains=list.contains(new String("qwer"));
        System.out.println(contains);
        System.out.println(contains?"包含元素'fds'":"不包含元素'fds'");
        //
      //  System.out.println(list.get(0));

节点操作

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();
        list.add(1);
        System.out.println("头结点:" + list.getFirst());
        System.out.println("尾结点:" + list.getLast());
        list.add(3);
        list.addFirst(2);
        System.out.println("头结点:" + list.getFirst());
        System.out.println("尾结点:" + list.getLast());
        list.addLast(4);
        System.out.println(list);//2,1,3,4
    }
}

3)Vector(向量,线程安全)

线程安全不常用

/**
 * Vector:线程安全,数组,和ArrayList类似
 * 常用方法:新增
 *      void addElement(Object);
 *      void insertElement(Object,int);
 *      void setElement(Object,int);
 *      void removeElement(Object);
 *      void removeAllElements();
 */
public class MyVector {
    public static void main(String[] args) {
        Vector vector = new Vector();
        vector.addElement(1);
        vector.add(0,2);
        System.out.println(vector);
    }
}

4、set接口(无序不可重复)

1)HashSet(HashMap的key部分,哈希和比较)

存储自定义类型需要重写hashcode和equals方法,不重写其实也可以,但是数组的散列不够均匀而且比较大小有问题。利用HashMap的key部分,可以存储null

/**
 *
 * Set:无序不可重复(采用map集合的key部分,equals判断),没有新增方法
 * 实现类HashSet: 实则是采用HashMap的key部分(key是不可重复的,HashCode确定数组下标,equals判断是否相等,不相等添加链表元素,相等替换),
 *                判断是否相等:元素hashcode值相同,并且equals相同
 *                存储实体类应该重写equals方法和hashcode
 */
public class MyHashSet {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        set.add(128);
        set.add(129);
        set.add(130);
        set.add(1);
        System.out.println(set.add(128));//插入失败
        System.out.println(set);//[128, 129, 1, 130]无序排列
        Integer i ;
    }
}

2)TreeSet(TreeMap的key部分,排序)

实际上是利用了TreeMap的key实现,只能存储一种类型,存储实体类还需要设置排序规则(自然排序,定制排序)。比较时也是通过排序进行比较

/**
 *
 * TreeSet:实现了SortedSet接口(可排序:自然排序,定制排序),红黑树,实际就是TreeMap的key部分
 *         排序:
 *               ①自定义类需要实现comparable接口,并且实现comparaTo(Object obj)方法,不然插入报错(java.lang.ClassCastException: com.zy.collection.set.Student cannot be cast to java.lang.Comparable)
 *                 基本数据类型都是比较值,常用类String比较unicode的值,常用类date比较时间大小
 *               ②定制排序,实体类实现Comparator接口,重写int compare(o1,o2),1:o1>o2,0,-1
 *         每插入一个元素就会和之前的元素比较,并且按照升序排序(第一个除外)
 *         插入的元素应该是同一种类型,不然无法比较
 * 常用方法:
 *      Comparator comparator();
 *      Object first();
 *      Object last();
 *      Object lower(Object);
 *      Object higher(Object);
 *      SortedSet subSet(Object toElement,Object endElement)
 *      SortedSet headSet(Object end);头结点到指定元素的集合,不包括结尾
 *      SortedSet tailSet(Object from);
 *
 */

public class MySortedSet {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
        set.add(1);
        set.add(new Integer(2));
        set.add(new Integer(5));
        set.add(new Integer(4));
        set.add(new Integer(3));
        System.out.println(set.first());
        //set = (TreeSet) set.headSet(3);//[1,2]
        //set = (TreeSet) set.tailSet(-1);//[1, 2, 3, 4, 5]
        System.out.println(set);//


        TreeSet<Student> treeSet = new TreeSet();
        treeSet.add(new Student(1,"2"));
        treeSet.add(new Student(2,"3"));
        treeSet.add(new Student(2,"3"));//相等
        System.out.println(treeSet);

    }
}

二、Map(双列集合,KV键值对存储)

1、UML

在这里插入图片描述

2、 Map接口

1)基础方法

KV存储,无序不可重复,可以存储null值,

/**
 * Map接口:KV键值对
 * 常用方法:
 *   Object put(Object,Object):KV存储
 *   boolean putAll(Map):存储map中的所有KV
 *   Object remove(Object key):删除指定key,返回value
 *   void clear():清空所有KV
 *
 *   Object get(Object):获取指定key的value
 *   boolean containsKey(Object):包含key
 *   boolean containsValue(Object):包含value
 *   int size():数据长度
 *   boolean isEmpay():是否为空
 *   boolean equals():比较是否相等???
 *
 *   Set keySet():返回所有key的Set集合
 *   Set<Map.Entry<Object,Object>> entrySet:返回存储KV信息的entrySet的set集合
 *   Collection values():返回所有values的collection集合
 *   
 *
 *
 */
public class MyMap {
    public static void main(String[] args) {
        Map<String,Object> map = new HashMap();
        map.put("k1","v1");
        map.put("k1","v1");
        map.put("k2","v2");
        System.out.println("大小:"+map.size());
        System.out.println("k1对应的value:"+map.get("k1"));//equals方法
        System.out.println("是否包含Key:k2->"+map.containsKey("k2"));//hashcode和equals相等
        System.out.println("是否包含Value:v2->"+map.containsValue("v2"));//比较equals
        System.out.println("是否为空:"+map.isEmpty());
        System.out.println("删除k1:"+map.remove("k1"));
        System.out.println("大小:"+map.size());
        System.out.println("清空map所有元素");
        map.clear();
        System.out.println("最终大小:"+map.size());
    }
}

2)操作方法
①Map.computeIfAbsent,computeIfPresent,compute,不存在key/存在key/无论如何都会进入方法,方法体内的对象作为返回值,并且写入对应key的value


2、HashMap(数组+链表+红黑树(链表大于8))

1、基本常量:
①数组容量:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
②加载因子:static final float DEFAULT_LOAD_FACTOR = 0.75f;
③最大容量:static final int MAXIMUM_CAPACITY = 1 << 30;
④扩容:static final int MIN_TREEIFY_CAPACITY = 64;
2、变量:
①KV键值对元素:transient Set<Map.Entry<K,V>> entrySet;
②存储链表的数组:transient Node<K,V>[] table;
③扩容和结构改变的次数:transient int modCount;
当链表长度大于该值转为红黑树:static final int TREEIFY_THRESHOLD = 8;
当红黑树节点小于该值转为链表:static final int UNTREEIFY_THRESHOLD = 6;
3、存储过程:根据存储类型的hashcode值经过计算得到hashmap中对应的数组下标,如果当前位置没有元素,那么这个元素直接被添加到链表的头部,如果有值,那么会比较hashcode是否相等,如果不等直接插入,如果相等,再比对equals是否相等,如果不等,插入,如果相等也就是有k存在,那么当前key的value会替换掉之前的value值
4、关于比较:比较的是hashcode和equals,(区别于list集合一个一个比较效率很高)
5、jdk1.7和jdk1.8:
JDK1.8之前,new对象时设置容量大小,JDK1.8put时设置容量大小,并且数组类型为Node,以前是entry,新增红黑树结构


在这里插入图片描述
5、代码测试

/**
 * key允许为null
 */
public class MyHashMap {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put(null,null);
        map.put(null,"1");//替换value
        map.put(1,2);
        System.out.println(map);[null=1,1=2]
    }
}

3、TreeMap(红黑树)

1、实现了SortedMap,所以可排序。对于基本数据类型比较值(对应的包装类型都实现了接口)和常用类String比较unicode的值,Date比较毫秒数。自定义类需要实现排序接口(Comparator,Comparable)。
2、只能存储一种类型,存储多种类型比较时会出现强转错误
3、put源码:
首先判断是否第一次添加数据,
—>是则初始化,直接添加第一个数据
–>不是,获取全局比较器
---->不为空,利用全局比较比较,左小右大
—>为空,利用key的实现比较器比较,左小右大

public V put(K key, V value) {
        Entry<K,V> t = root;
        //在第一次添加数据时初始化
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        //通过new对象指定的Comparator比较器规则比较
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //比较器规则为空,则通过Key实现的比较器进行比较,左小右大
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

4、Hashtable(线程安全)

和HashMap算法一样,只是是线程安全的,key和value不允许为null

/**
/**
 * key,value均不允许为null
 */
public class MyHashtable {
    public static void main(String[] args) {
        Hashtable hashtable = new Hashtable();
        //hashtable.put(null,null);
      //  hashtable.put(null,1);
        hashtable.put(1,null);
    }
}

5、Properties(IO操作,只能存储String类型)

1、只能存储string,String类型
2、多用于IO流读写配置文件
3、常用方法
①load(InputStream)读取KV键值对
②store(OutputStream)写入KV键值对

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        FileOutputStream fos = new FileOutputStream("test.txt");
        FileInputStream fis = new FileInputStream("test.txt");

        properties.setProperty("name","properties");
        properties.setProperty("age","12");
        properties.store(fos,"");
        properties.clear();//清空数组内容
        properties.load(fis);
        System.out.println("name:"+properties.getProperty("name")+","+"age:"+properties.getProperty("age"));
        fos.close();
        fis.close();
    }
}

6、LinkedHashMap(有序遍历输出)

在添加数据时,利用双向链表指针指向下一个元素,遍历时通过指针顺序输出

public class LinkedMap {
    public static void main(String[] args) {
        LinkedHashMap map = new LinkedHashMap();
        map.put(2,1);
        map.put(1,1);
        map.put(5,1);
        map.put(3,1);
        HashMap map2 = new HashMap(map);
        System.out.println(map);//2,1,5,3
        System.out.println(map2);//1,2,3,5
    }
}

7、关于HashMap存储引用数据类型时的比较

存储自定义类总是需要重写hashcode(决定数组下标)和equals方法(决定对象是否相等)
重写规则:
①两个相同对象无论调用多少次hashcode方法都是相等的
②两个不同的对象如果equals相等,那么hashcode也应该是相同的
当对于存储自定义数据类型时,应该具有一定的逻辑关系,比如学生类,判断的是学号是否相同,hashcode应该与学号有关,所以与equals有关的field都应该和hashcode影响(在使用IDEA工具重写时,可以看到重写两个方法时,与equals相关的field也必须影响hashcode),所以我们需要同时重写hashcode和equals方法

8、关于TreeMap的Comparator和Comparable

定制排序:自定义类实现Comparator接口,然后将对象传入到map的构造方法中,
自然排序:map存储的实体类实现Comparable接口,重写排序方法,
调用时,首先是获取定制排序Comparator进行排序的,没有才会去找存储类Comparable接口的比较方法

三、Iterator迭代器

1、主要用来遍历Collection集合中的元素,Collection继承了Iteratable接口,通过实现类的iterator方法可以返回迭代器Iterator对象
2、过程解读
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(-1);
        list.add(0);
        Iterator<Integer> iterator = list.iterator();//获取迭代器对象,指针指向第一个元素之前
        System.out.println(list);
        while(iterator.hasNext()){//是否到达数组末尾
            int a = iterator.next();//获取下一个数据,并且指针后移
            if(a==0) iterator.remove();//删除集合中所有的0
        }
        System.out.println(list);
    }
}

四、Collections工具类

1、操作java中的集合(map和collection)
2、将线程非安全的集合转为线程安全的

/**
 *常用方法:
 *   void reverse(List<?>):顺序反转
 *   void shuffle(List<?>):随机排序
 *   sort(List):根据元素的自然顺序排序
 *   sort(List,Comparator):根据定制排序排序
 *   Object max/min(Collection):根据自然排序获取元素中最大值/最小值
 *   Object max/min(Collection,Comparator):指定排序规则获取最大值/最小值
 *   void copy(List dest,list src):将src复制到dest中
 *   boolean replaceAll(List,Object oldVal,Object newVal)
 *   XXX synchronizedXXX(XXX):将指定的集合转换成线程安全的并且返回
 *
 *
 */
public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1);
        System.out.println(list);
        Collections.reverse(list);//顺序反转
        //Collections.shuffle(list);//随机排序
        System.out.println("反转list:"+list);//3,1,2
        Collections.swap(list,0,1);//交换指定两个下标的元素。
        System.out.println("交换0,1"+list);
        Collections.sort(list);
        System.out.println("自然排序:"+list);
        System.out.println("元素最大值:"+Collections.max(list));
        Collections.replaceAll(list,1,0);
        System.out.println("替换所有的1为0"+list);
        List list2 = Collections.synchronizedList(list);
    }
}

五、练习

1)list去重

public class work08_RemoveSame {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1);
        list.add(2);
        System.out.println(list);
        Set set = new TreeSet();
        set.addAll(list);//去除重复
        list = new ArrayList(set);
        System.out.println(list);

    }
}

2)

总结

1、集合只能存储引用数据类型(基本数据类型自动装箱),在map中,存储之后不要修改其属性(与equals和hashcode相关),修改后的hashcode值改变了,再和之前的自己比较就是不同的了,也就是对于HashMap比较的是hashcode和equals
2、Set只能存储自定义类:
HashSet需要重写hashcode和equals
TreeSet需要确定排序规则(Comparator,Comparable)
set集合只能存储一种数据类型,不然比较时会出错

3、数组转listArrays.asList(),这样也可以撑开list的大小,不用自己一一add,也别是在使用Collections.copy()方法时。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值