(2021-07-12)Java集合、Collections和Arrays工具类

16-集合

什么是集合

定义:

​ 集合一个存储对象的容器,面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,存储对象,那么集合是存储对象最常用的方式之一。
​ 集合的出现就是为了持有对象。集合中可以存储任意类型的对象,而且长度可变,在程序中有可能无法预知需要多个对象,那么如果用数组存储的话,长度不好定义,集合刚好解决了这个问题。

集合和数组的区别

1、集合和数组都是容器

2、数组的长度是固定的,集合的长度是可变的

3、数组中只能存放单一的数据类型,而集合可以存储不同类型的对象【集合只能存储对象】。

4、数组对于CRUD(增删改查),操作复杂,但是集合相对方便很多。

集合的分类

Collection:单列集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qcRLICZw-1626698029329)(images/29.jpg)]

——Collection:单列集合

——List:有顺序地存储、可重复,是列表类集合

​ ——ArrayList:数组列表实现,特性:查找快、增加删除慢。
​ 原因:由于是数组实现,在增加和删除操作时会牵扯到数组扩容,以及拷贝元素,所以慢;但是数组可以按照索引查询,所以查询快。

​ ——LinkedList:链表类型实现,特性:增加删除快,查找慢。

​ 原因:由于链表实现,增加时只要让前一个元素记住自己就可以了,删除时让前一个记住后一个元素,这样增加删除就快,但是查询时需要一个一个遍历,所以查询效率会比较低。

​ ——Vector:和ArrayList原理相同,实现和操作方式也相同,但线程安全

——Set:无顺序存储、不可重复的

​ ——HashSet:以哈希值存储,但是不保留顺序,并且它可以去掉重复元素。

​ ——TreeSet:以树型结构存储,它会将元素排序,但是效率没有HashSet高。

​ ——LinkedHashSet:既可以保留存储顺序,又可以过滤掉重复元素。

Map:键值对(key-value)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jbtCKlAu-1626698029331)(images/30.jpg)]

——Map:键值对(key、value)

​ ——HashMap:

​ ——LinkedHashMap:

​ ——TreeMap:

​ ——HashTable:

**PS:**为什么会出现这么多个集合容器:是因为每一个容器对于数据的存储方式不同,而这种存储方式我们将其称为【数据结构】。

  • List、Set元素在添加、修改是都会把类型转为Object,所以获取出来的类型也是Object,如果要操作获取出来的数据,需要进行一次类型强 转。
  • Map中的key、value在添加、修改是都会把类型转为Object,所以获取出来的类型也是Object,如果要操作获取出来的数据,需要进行一次类型强转。

不同类型的集合的使用场景

Collection需要保留若干个对象的时候,使用该Connection
List需要保留存储顺序,并且保留重复元素,使用List
—如查询较多,使用ArrayList
—如增删较多,使用LinkedList
—如要线程安全,使用Vector
Set不需要保留存储顺序,并且需要去除重复元素,使用Set
—如需将元素排序,使用TreeSet
—如不需将元素排序,使用HashSet
—如需保留存储顺序,又要过滤重复元素,使用LinkedHashSet

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSU9wWjp-1626698029332)(images/28.jpg)]

Collection共有的操作方法

操作名方法名方法描述
增加add()将指定的对象存储到容器中,add方法的参数类型是object类型,便于接收任意对象
addAll()将指定集合的元素添加到调用该方法的集合中
删除remove()将指定的对象从集合中移除
removeAll()将指定的集合中所有元素移除
clear()清空集合中所有元素
查询isEmpty()判断集合是否为空
contains()判断集合中是否包含指定的对象
containsAll()判断集合中是否包含指定集合
size()返回集合容器的大小
转换为数组toArray()集合转换成数组
package package02;

import java.util.ArrayList;
import java.util.Collection;

/**
 * @Description java集合:集合共性的操作方法
 * @Author WENG Jun
 * @Date 2021/7/12 - 15:33
 */
public class CollectionsTest {
    public static void main(String[] args) {
        Collection list = new ArrayList();

        //add():将指定的对象存储到容器中
        list.add("java");
        list.add("html");
        list.add("css");
        System.out.println(list);//[java, html, css]

        //addAll():将指定集合的元素添加到调用该方法的集合中
        Collection list2 = new ArrayList();
        list2.add("spring");
        list2.addAll(list);
        list2.add("spring boot");
        System.out.println(list2);//[spring, java, html, css, spring boot]

        //remove()将指定的对象从集合中移除
        System.out.println(list2.remove("spring boot"));//true
        System.out.println(list2);//[spring, java, html, css]

        //removeAll()将指定的集合中所有元素移除
        list2.removeAll(list);
        System.out.println(list2);//[spring]

        //clear()清空集合中所有元素
        list2.clear();
        System.out.println(list2);//[]

        //isEmpty()判断集合是否为空
        System.out.println(list2.isEmpty());//true

        //size()返回集合容器的大小
        System.out.println(list.size());//3

        //contains()判断集合中是否包含指定的对象
        System.out.println(list.contains("java"));//true

        //containsAll()判断集合中是否包含指定集合
        Collection list3 = new ArrayList();
//        list3.add("hello");
        list3.add("java");
        System.out.println(list.containsAll(list·3));//true

    }
}

List接口

List集合特有的方法

操作名方法名方法描述
增加void add(int index,E element)在指定位置添加元素
boolean addAll(int index, Collection c)在指定位置添加集合
删除E remove(int index)删除指定位置元素
修改E set(int index ,E element)返回的是需要替换的集合的元素,返回被操作的对象
查找E get(int index)根据元素索引查找,要注意角标越界,返回被操作的对象
int indexOf(Object obj)根据对象做匹配,如找到,返回索引,否则返回-1
int lastIndexOf(Object obj)最后一个对象的位置
package package02;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @Description List特有的方法、迭代器
 * @Author WENG Jun
 * @Date 2021/7/12 - 16:17
 */
public class ListTest {
    public static void main(String[] args) {
        List list = new ArrayList();

        list.add("java编程思想");
        list.add("java核心技术");
        list.add("java程序设计");

        //void add(int index,E element)在指定位置添加元素
        list.add(0, "格林童话");
        System.out.println(list);//[格林童话, java编程思想, java核心技术, java程序设计]

        ArrayList list2 = new ArrayList();
        list2.add("西游记");
        list2.add("水浒传");
        list2.add("三国演义");
        list2.add("红楼梦");

        //boolean addAll(int index, Collection c)在指定位置添加集合
        System.out.println(list.addAll(1, list2));//true
        System.out.println(list);//[格林童话, 西游记, 水浒传, 三国演义, 红楼梦, java编程思想, java核心技术, java程序设计]

        //E remove(int index)删除指定位置元素
        list.remove(4);//true
        System.out.println(list);//[格林童话, 西游记, 水浒传, 三国演义, java编程思想, java核心技术, java程序设计]
        list.remove("三国演义");
        System.out.println(list);//[格林童话, 西游记, 水浒传, java编程思想, java核心技术, java程序设计]

        //E set(int index ,E element)返回的是需要替换的集合的元素
        Object o = list.set(0, "深入理解JVM虚拟机");
        System.out.println(o);//格林童话 返回被替换掉的元素
        System.out.println(list);//[深入理解JVM虚拟机, 西游记, 水浒传, java编程思想, java核心技术, java程序设计]

        //E get(int index)根据元素索引查找,要注意角标越界,返回被操作的对象
//        Object o1=list2.get(10);//角标越界
        Object o1 = list2.get(0);//角标越界
        System.out.println(o1);//西游记

        //int indexOf(Object obj)根据对象做匹配,如找到,返回索引,否则返回-1
        System.out.println(list2.indexOf("西游记1"));//找不到,返回-1

        //int lastIndexOf(Object obj)最后一个对象的位置
        System.out.println(list.lastIndexOf("java核心技术"));//4

        //迭代器
        Iterator it = list2.iterator();
        while (it.hasNext()) {
            String str = (String) it.next();
            System.out.print(str);//西游记水浒传三国演义红楼梦
        }

    }
}

ArrayList的实现原理

**原因:**因为ArrayList底层维护的是一个Object[]数组,用于存储对象,该数组的默认长度为10,也可以手动更改这个数组的长度,在初始化的时候:new ArrayList(20);

不设置长度没有关系,因为默认或指定的容量不足的时候,容量自动增长到原来的1.5倍((oldCapacity*3)/2+1)。

总结:已知ArrayList是数组实现,在增加和删除时会牵扯到数组扩容,以及拷贝元素,所以增删效率低下,但数组按照下标索引查找,所以查找时效率高。

LinkedList实现原理

原因:底层采用双向链表形式,在内存中的地址不是连续的,它是由上一个元素去记住下一个元素,所以每一个元素保存的有下一个元素的位置,虽然也有下标索引,查找时需要从头往下找,这个方式显然没有数组查找快,但是,链表在插入一个新元素的时候,只需要让前一个元素记住这个新元素,让新元素记住下一个元素就可以,所以增删很快。

操作名方法名方法描述
添加addFirst(E element)添加在首个位置
addLast(E element)追加到末尾
删除removeFirst()删除首个元素
removeLast()删除末尾元素
查找getFirst()得到第一个元素
getLast()得到最后一个元素
逆序输出迭代器对象descendingIterator()返回逆序的迭代器对象
package package02;


import java.util.Iterator;
import java.util.LinkedList;

/**
 * @Description LinkedList特有方法
 * @Author WENG Jun
 * @Date 2021/7/12 - 16:17
 */
public class LinkedListTest {
    public static void main(String[] args) {
        LinkedList list2 = new LinkedList();

        list2.add("java编程思想");
        list2.add("java核心技术");
        list2.add("java程序设计");

        //removeFirst()删除首个元素
        list2.removeFirst();

        //removeLast()删除末尾元素
        list2.removeLast();

        //addFirst(E element)添加在首个位置
        list2.addFirst("你好,生活!");

        //addLast(E element)追加到末尾
        list2.addLast("Hello Java!");

        //getFirst()得到第一个元素
        System.out.println(list2.getFirst());//你好,生活!

        //getLast()得到最后一个元素
        System.out.println(list2.getLast());//Hello Java!


        //逆序迭代
        Iterator it = list2.descendingIterator();
        //迭代器遍历输出
        while (it.hasNext()) {
            System.out.print(it.next() + " ");//Hello Java! java核心技术 你好,生活! 
        }
    }
}

Vector的实现原理

Vector:其就是一个线程安全的ArrayList。

ArrayList:单线程,效率高,但不安全,因为每一个用户进来都可以去抢。

Vector:多线程,线程安全,但效率低,因为需要等待。

操作名方法名方法描述
添加void addElement(Object obj)在集合的末尾添加元素
查找E elementAt(int index)返回指定下标的元素
Enumeration elements()返回集合中的所有元素,封装到Enum对象中去
Enumeration接口
boolean hasMoreElements()测试此枚举类是否包含元素
E nextElement()如果此枚举对象至少还有一个可提供的元素,则返回此元素
package package02;

import java.util.Enumeration;
import java.util.Vector;

/**
 * @Description Vector
 * @Author WENG Jun
 * @Date 2021/7/12 - 17:25
 */
public class VectorTest {
    public static void main(String[] args) {
        Vector v = new Vector();

        v.addElement("aaa");
        v.addElement("bbb");
        v.addElement("ccc");

        System.out.println(v);//[aaa, bbb, ccc]

        //E elementAt(int index)返回指定下标的元素
        System.out.println(v.elementAt(2));//ccc


        //Enumeration接口:
        //Enumeration elements()返回集合中的所有元素,封装到Enum对象中去
        Enumeration ens = v.elements();
        //boolean hasMoreElements()测试此枚举类是否包含元素
        //迭代器遍历输出
        while (ens.hasMoreElements()) {
            //E nextElement()如果此枚举对象至少还有一个可提供的元素,则返回此元素
            System.out.print(ens.nextElement() + " ");
        }
        //aaa bbb ccc
    }
}

迭代器Iterator

描述:

为了方便地处理集合中的元素,java出现了一个对象,该对象专门用于处理集合中的元素,例如删除和获取集合中的元素,这个对象就是迭代器Iterator。

作用:

因为它更加符合面向对象的思想,就是专业的对象做专业的事情,那么迭代器就是专门取出集合元素的对象,但是该对象比较特殊,不能直接创建对象(不能通过new来实现),该对象是以内部类的形式存在于每个集合类的内部。

如何获取迭代器?

Collection接口中定义了获取迭代器的方法(Iterator();),所以所有的Collection体系的接口都可以获取自身的迭代器。

由于每一个容器都有取出元素的功能,但这些功能定义都一样,只不过实现的具体方法不相同(因为每一个容器的数据结构不一样),所以对于共性的取出功能进行了抽取,从而实现了Iterator接口,让每一个容器都在其内部对该接口进行了内部类的处理,也就是说将其取出的方法细节做了封装。

Iterable:

jdk1.5之后添加的新接口,也是Collection的父接口,实现了Iterable的类就是可以迭代的,并且增加了一个叫做支持增强for循环。该接口只有一个方法,就是获取迭代器的方法iterator(),可以获取每一个容器自身的迭代器。Collection集合容器在5.0之后又进行了一次抽取,它将获取容器迭代器的方法放到了Iterable接口中。

forEach

描述:

是一种简洁而有趣的迭代集合的方式,那么通过它的名字我们就能看出,for循环,Each每一个,那么我们看到forEach一般来说会和集合或者数组一起使用,但不是所有的forEach都适合,(forEach能实现的,for都可以实现,反之,不一定)

它也是Iterable的一个接口:

package package01;

import java.util.ArrayList;
import java.util.List;

public class ForEachTest {
    public static void main(String[] args) {
        List str = new ArrayList();
        str.add("张三");
        str.add("李四");
        str.add("王五");

        //普通for循环
        for (int i = 0; i < str.size(); i++) {
            System.out.println(str.get(i));
        }

        //forEach增强for循环
        for (Object obj : str) {
            System.out.println(obj);

        }

        //lambod表达式
        str.forEach(s -> {
            System.out.println(s);
        });

        //lambod的精简写法
        str.forEach(System.out::println);
    }
}

迭代器接口的定义方法:

操作名方法名方法描述
判断boolean hashNext();判断集合中是否有元素,如有元素返回true,一般结合循环使用
获取E next();返回迭代的下一个元素,请注意:如果没有下一个元素时,强行调用next,会抛出一个异常,NoSuchElementsException异常
删除void remove();从迭代器指向的集合中移除迭代器返回的最后一个元素
List str = new ArrayList();
str.add("张三");
str.add("李四");
str.add("王五");

while循环方式:

//while循环方式
while (it1.hasNext()) {
    String s = (String) it1.next();
    System.out.println(s);
}

for循环方式:

//for循环方式
for (Iterator it2 = str.iterator(); it2.hasNext(); ) {
    String s = (String) it2.next();
    System.out.println(s);
}

remove:

//remove方法
        while (it1.hasNext()) {
            String s = (String) it1.next();
            //删除李四
            if (s.equals("李四")) {
                it1.remove();//必须先取值在删除,否则报错
            }
        }

注意细节:

1.如果迭代器的指针已经指向了集合的末尾,那么再调用next(),会报NoSuchelementException异常;

2.如调用remove之前没有调用next方法,这个操作对于迭代器来说是不合法的,会报IllegalStateexception异常;

3.在对集合进行迭代的过程中,不允许出现迭代以外对元素的操作,因为这样会产生安全隐患,java会报异常,并发修改异常(ConcurrentModificationException),普通迭代器只支持在迭代过程中的删除动作

List专有的迭代器ListIterator

**描述:**该接口是继承了Iterable

操作名方法名方法描述
添加add(E e);将指定的元素插入列表,该元素直接插入到next返回的下一个元素前面(如果有的话)
修改void set(E o);用于指定元素替换next或者previous返回的最后一个元素
获取Object previous();获取列表中的上一个元素(如果有的话)
查询boolean hasPrevious();逆向遍历列表,列表迭代器有多个元素时,返回true
package package01;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ListIteratorTest {
    public static void main(String[] args) {
        List str = new ArrayList();
        str.add("张三");
        str.add("李四");
        str.add("王五");

        ListIterator it = str.listIterator();

        //普通写法
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        System.out.println("--------------------------");
        //逆序写法
        while (it.hasPrevious()){
            System.out.println(it.previous());
        }

        System.out.println("--------------------------");
        //替换李四,正序输出
        while (it.hasNext()) {
           String s= (String) it.next();
           if (s.equals("李四")){
               it.set("秀儿");
           }
        }
        System.out.println(str);

        System.out.println("--------------------------");
        //添加
        while (it.hasNext()) {
            String s= (String) it.next();
            if (s.equals("李四")){
                it.add("秀儿");
            }
        }    
    }
}

Iterator与ListIterator主要区别:

  • 都有hasNext()、next()方法,可以实现顺序遍历。但ListIterator有boolean hasPrevious()、Object previous() 可以实现逆向遍历
  • ListIterator可以定位当前元素的索引尾椎,方法int nextIndex()、int previousIndex(),Iterator无此功能;
  • ListIterator有add()方法,可以向List中插入元素,Iterator无此方法;
  • 都可以删除元素,当ListIterator可以用 set(Object e) 修改元素对象,因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。

Set接口

描述

​ 它注重的是独一无二的性质,该体系集合可以知道某个值是否已经存在于集合中,不会存储重复的元素,并且它也是存储无序(存入和取出的顺序不相同)的元素。

Set为什么不能存储重复的数据?

​ 是因为对象的相等性,因为如果引用到堆上面,是同一个对象的话,那么它的两个引用是相等的。

​ 首先会去比两个对象的hashCode值,比较的对象所有的类如果没有覆盖Object的hashCode()方法的话,那么hashCode()会返回每个对象特有的序号(java依据对象的内存地址值计算出次序号)。

​ 也就是说,在不重写hashCode()前提下,两个对象的hashCode值是不可能相等的(极小概率可能出现相等)。

​ 再用equals方法比较,如果两个对象的地址值也不相同,那么Set才会将值放入到我们的集合中。

Set集合中没有特有的方法,直接继承它的父类Collection

HashSet

可以帮我们去除存储的数据中重复的元素,但是不能排序。

HashSet的底层是HashMap

package package02;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/*
    【Set接口】

----Set接口:存储无序的、不可重复的数据------>高中讲的"集合"
    ----HashSet:作为Set接口的主要实现类,线程不安全,可以存储null值
        ----LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加顺序输出
    ----TreeSet:可以按照添加对象的指定属性,进行排序

    1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法

    2.要求:向Set中添加数据,其所在的类一定要重写hashCode()和equals()方法。
            重写hashCode()和equals()方法尽可能保持一致性:相等的对象必须具有相等的散列码。

            重写两个方法的小技巧:直接使用编译器生成的方法,哈哈哈哈!


 */
public class SetTest {
    /*
     一、Set:存储无序的、不可重复的数据
        (以HashSet为例):
         1.无序:不等于随机性,存储数据时的顺序不是添加的顺序,根据数据的Hash值决定位置

         2.不可重复:保证添加的元素按照equals()判断时,不能返回true
                    即相同的元素只能出现一次,先比较Hash值在比较equals,两个都相同则不能添加

     二、添加元素的过程:
        (以HashSet为例):
                我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
                此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
                数组的此位置上是否有元素:
                    如此位置上没有其他元素,则元素a直接添加成功。 ----情况1
                    如此位置有其他的元素b(或以链表形式存在多个个元素),则比较元素b和元素a的哈希值:
                        如hash值不相同,则元素a添加成功。 ----情况2
                        如hash值相同,进而还需要调用元素a所在类的equals()方法:
                             equals()返回false,元素a添加成功。   ----情况3
                             equals()返回true,元素a添加失败

                     注意:对于添加成功的情况2、3:
                        元素a与已经存在指定的索引位置上的数据以链表方式存储:
                            JDK7:元素a放在数组中,指向原来的元素  b<---a
                            JDk8:原来的元素在数组中,指向元素a    a<---b
                            总结:7上8下

            HashSet底层:数组+链表的结构


     */
    public static void main(String[] args) {
        Set set = new HashSet();

//        set.add("张三");
//        set.add("李四");
//        set.add("王五");
//
//        //取集合的大小
//        System.out.println(set.size());
        System.out.println(set.get(2));//没有get方法
//
//        //不能添加重复元素
//        System.out.println(set.add("张三"));//false
//
//
//        //forEach遍历输出
//        for (Object o : set) {
//            System.out.println(o);
//        }
//
//        System.out.println();
//
//        //迭代器遍历输出
//        Iterator it = set.iterator();
//        while (it.hasNext()) {
//            System.out.println(it.next());
//        }

        set.add(new Person("张三", 18));
        set.add(new Person("李四", 20));


        for (Object o : set) {
            System.out.println(o);
        }

        boolean flag = set.add(new Person("张三", 18));//不能添加重复的元素
        System.out.println(flag);//false
    }
}

TreeSet

是以树形结构作为排序方式,红黑树,红黑树是一种特定类型的二叉树

红黑树的算法规则:左小右大

遵循的原则:

1.存入的元素自身具有比较性(自然排序)

 			  TreeSet ts = new TreeSet();
        ts.add("CC");
        ts.add("AA");
        ts.add("DD");
        ts.add("BB");

        System.out.println(ts);//[AA, BB, CC, DD]

为什么使用TreeSet存入字符串是按照升序的顺序排列的呢?

​ 因为TreeSet字符串实现了一个接口,这个接口叫做Comparable接口,重写了该接口的compareTo()方法,所以String对于该操作具有了比较性,同理,例如自定义的对象(Person、Order等类的实例),就需要实现该接口,也就是让自定义对象具有比较规则。

2.让存入的元素自定义比较规则(定制排序)

注意:在重写compareTo()和compare()方法时,必须要明确比较条件相等时,要比较的次要条件(假设:姓名和年龄一致的人为相同一个人,如果按照此方式,那么无法进行处理,不能直接return 0,所以就需要使用次要条件来判断)。

 //重写compareTo()方法
    @Override
    public int compareTo(Object o) {
        Person p = (Person) o;
        //先比姓名
        if (this.name.compareTo(p.name) == 0) {
            //再比年龄
            if (this.age == p.age) {
                // 再比sid
                if (this.sid.equals(p.sid)) {
                    //全部相等返回0,说明是同一个对象
                    return 0;
                }
            }
        }
        //否则返回-1,说明不是同一个对象
        return -1;
    }

共性的问题:

1、compareTo和equals的区别:

​ equals比较的是地址值,返回true和false;

​ compareTo()返回的是三个值,1,0,-1,是根据编码的数值求取差值,能够确定两个String在字典顺序上的前后性,根据这个排列,能够将这个差值返回回去。所以equals通常用在对象的比较,而compareTo用在String类型的比较,也就是,例如:

value1.compareTo(value2),当value1不是String类型时,会报错。

总结:

(1)**==:**基本上用于基本数据类型的比较,8个基本数据类型。

(2)**equals:**引用数据类型的比较(除了上面的8个),包括这8个的包装类、Object的子类、自定义类型 ,它比较的时地址值。

(3)**compareTo:**一般用于比较两个字符串并且得到顺序,按照字典顺序比较,该比较的是基于字符串和各个字符的Unicode编码值。

【练习】:使用TreeSet做一个将字符串排序的练习

​ 例如:String str=”9 10 15 2 3 8“;

思路:

(1)将字符串切割,根据空格切割,切出来一个String类型的字符数组。

(2)for循环遍历,把数组中的元素添加到TreeSet中(需要转换成包装类)

(3)输出TreeSet。

package package02;

import java.util.TreeSet;

public class TreeSetExer {
    public static void main(String[] args) {
        String str = "9 10 15 2 3 8";
        String[] s = str.split(" ");

        TreeSet ts = new TreeSet();
        for (int i = 0; i < s.length; i++) {
            ts.add(Integer.parseInt(s[i]));
        }

        System.out.println(ts);

//        ts.forEach(System.out::println);
    }
}

LinkedHashSet

链表实现,可以保存存储时的顺序,也就是说,它是Set当中唯一的一个能保证怎么存就能么取的集合对象。

由于它也是HashSet的子类,所以也可以可以保证元素的唯一性,这个和HashSet原理相同。

单一列表Collection的总结

1.看到Array,就要想到下标

2.看到Linked,就要想到链表,first,last

3.看到Hash,就要想到hashCode和equals

4.看到Tree就要想到两个接口,comparable和comparator

Map接口

描述:

Map是一个依照键(key)和值(value)的形式来存储元素数据的容器。key很像一个下标,它可以存储任意类型的对象,不能有重复的key,每一个key都有一个对印的value,一个key和一个value构成了Map集合中的一个元素。

Map中的元素:

key/value,一般用对象作为键,一般也用对象作为值,键不可以重复,值可以重复。

(1)Map和Collection在集合框架中是并列存在的,但是Map不可以直接用循环来解析的。

(2)Collection是单列集合,Map是双列集合。

(3)Collection根据数据结构的不同,有些可以重复,有些不能重复,Map集合中的键要保证唯一。

(4)如果要取出Map当中的元素,是无法直接取出的,需要先转换成Set集合,再通过Set集合大方式,取出元素。

(5)Map一次存入一对元素,Collection一次存入一个元素。

作用:

interface Map<key,value>

key——映射所维护的键类型(通过key可以找到value)

value——映射的值类型

Map分类

(1)HashMap:采用哈希表实现,并且无序,线程不同步(线程不安全),可以存入null值

​ ——LinkedHashMap:这个子类基于哈希表,又融入了链表,可以对Map集合进行增删,提高效率。

(2)TreeMap:对于键可以排序,底层是二叉树的数据结构,可以对Map集合中的键进行排序,需要使用comparable或者comparator进行比较排序,并且保持唯一性。

(3)HashTable:底层也是哈希表数据结构,线程是同步的(线程安全),但是不可以存入null值,效率较低。

Map共有的方法

操作名方法名方法描述
添加put(key,value);可以赋值相同的key,但是添加的value值会覆盖前面value值(直接覆盖掉原有的值),也就是说,put方法不仅可以添加,还可以修改(覆盖)
putAll(Map<key,value>);从指定映射中将所有映射关系复制到此映射中
删除remove();删除关联对象,需要指定key对象
clear();清空集合对象
获取value get(key);通过key获取value,也可以用于判断键是否存在的情况,当指定的键不存在时,返回的是null
int size();获取集合的长度
判断boolean isEmpty();判断长度是否小于等于0,返回true或者false
containsKey(Object key);判断集合中是否包含指定的key
containsValue(Object value);判断集合中是否包含指定的value
package package03;

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        Map m1 = new HashMap();

        //添加put(key,value);可以赋值相同的key,但是添加的value值会覆盖前面value值
        m1.put("曹操", 20);
        m1.put("吕布", 18);
        m1.put("刘备", 16);
        m1.put("董卓", 13);

        System.out.println(m1);//{董卓=13, 吕布=18, 刘备=16, 曹操=20}

        Map m2 = new HashMap();
        m2.put("赵云", 23);
        m2.put("关羽", 22);

        //putAll(Map<key,value>);从指定映射中将所有映射关系复制到此映射中
        m1.putAll(m2);
        System.out.println(m1);//{董卓=13, 关羽=22, 吕布=18, 刘备=16, 曹操=20, 赵云=23}

//        //remove();删除关联对象,需要指定key对象
//        System.out.println(m1.remove("董卓"));
//        System.out.println(m1);//{关羽=22, 吕布=18, 刘备=16, 曹操=20, 赵云=23}

        //清空 clear() 清空集合对象
        m2.clear();
        System.out.println(m2);//{}

        //value get(key);通过key获取value,也可以用于判断键是否存在的情况,当指定的键不存在时,返回的是null
        System.out.println(m1.get("董卓11"));//null

        //int size();获取集合的长度
        System.out.println(m1.size());//6

        //boolean isEmpty();判断长度是否小于等于0,返回true或者false
        System.out.println(m1.isEmpty());//false

        //containsKey(Object key);判断集合中是否包含指定的key
        System.out.println(m1.containsKey("董卓"));//true

        //containsValue(Object value);判断集合中是否包含指定的value
        System.out.println(m1.containsValue(22));//true

    }
}

遍历Map的方式

操作名方法名方法描述
通过取key来获取元素SetkeySet();返回所有的key对象的set集合,再通过get方法获取key对应的value值
获取所有的value值Collection(v) values();返回Map集合中所有的value值,但是不能获取到key对象
Map.Entry对象【重点】Set<Map.Entry<k,v>> entrySet();将map集合中的映射关系,打包成一个对象,通过Map.Entry对象来获取集合中的元素
//Map的遍历方式

        //第一种方式(keySet):Set<k>keySet();返回所有的key对象的set集合,再通过get方法获取key对应的value值
        Set key = m1.keySet();
        for (Object o : key) {
            System.out.println(o + "=" + m1.get(o));
        }

        //第二种方式(values):Collection(v) values();返回Map集合中所有的value值,但是不能获取到key对象
        Collection values = m1.values();
        for (Object o : values) {
            System.out.println(o);
        }

        //第三种【重点】(entrySet):Set<Map.Entry<k,v>> entrySet();

//        Set set = m1.entrySet();
//        for (Object o : set) {
//            System.out.println(o);
//        }

        Set<Map.Entry> entry = m1.entrySet();
        Iterator<Map.Entry> it = entry.iterator();
        while (it.hasNext()) {
            Map.Entry me = it.next();
            System.out.println(me.getKey() + " " + me.getValue());
        }

TreeMap

TreeMap的排序,其实就是对集合中的键进行排序,是如何排序的?

1.元素自身具备比较性

​ 和TreeSet的原理相同,这种方式就是自然排序也是默认排序

2.容器具备比较性

​ 需要定义一个类实现Comparator接口,重写compare方法,并将该接口的子类实现对象将参数传递给TreeMap集合的构造方法。

【练习】使用HashMap和TreeMap分别解析下面的数据,要求按照原样输出,顺序不许乱(答案见下面的HashMap自定义排序)

tm.put("1","箱包");
tm.put("1.1","皮包");
tm.put("1.2","行李箱");
tm.put("2","衣帽");
tm.put("2.1","上衣");
tm.put("2.2","裤子");

HashMap自定义排序

package package03;


import java.util.*;

/*

【HashMap自定义排序】

练习:使用HashMap和TreeMap分别解析下面的数据,要求按照原样输出,顺序不许乱
        tm.put("1","箱包");
        tm.put("1.1","皮包");
        tm.put("1.2","行李箱");
        tm.put("2","衣帽");
        tm.put("2.1","上衣");
        tm.put("2.2","裤子");
 */

public class TreeMapExer implements Comparator<Map.Entry<String, String>> { //实现Comparator接口,类型为Map.Entry<String k,String v>
    public static void main(String[] args) {

        //创建TreeMap集合
        TreeMap tm = new TreeMap();
        tm.put("1", "箱包");
        tm.put("1.1", "皮包");
        tm.put("1.2", "行李箱");
        tm.put("2", "衣帽");
        tm.put("2.1", "上衣");
        tm.put("2.2", "裤子");

        System.out.println("TreeMap输出:");
        System.out.println(tm);

        //创建HashMap集合
        HashMap hm = new HashMap();
        hm.put("1", "箱包");
        hm.put("1.1", "皮包");
        hm.put("1.2", "行李箱");
        hm.put("2", "衣帽");
        hm.put("2.1", "上衣");
        hm.put("2.2", "裤子");

        System.out.println("HashMap原始输出:");
        System.out.println(hm);

        //获取HashMap集合的所有"映射"的Set集合,这里规范每个映射的类型为Map.Entry<String K,  String V>
        Set<Map.Entry<String, String>> entrySet = hm.entrySet();

        新建ArrayList集合,用于获取Set集合的所有元素("映射"对象)(顺序与Set集合一样)
        List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(entrySet);

        //通过“比较器(TreeMapExer)”,对ArrayList进行排序
        Collections.sort(list, new TreeMapExer());

        System.out.println("HashMap自定义排序输出:");
        //获取List集合的迭代器,Map.Entry< String K, String V>为迭代元素的类型
        Iterator<Map.Entry<String, String>> iter = list.iterator();
        while (iter.hasNext()) {
            //Map.Entry对象接收迭代器中下一个元素
            Map.Entry item = iter.next();
//            System.out.print(item.getKey() + "=" + item.getValue()+" ");
            System.out.println(item);
        }

//        for (Map.Entry<String, String> item : list) {
//            System.out.print(item.getKey() + "=" + item.getValue()+" ");
//        }


    }

    //重写compare()方法
    //传入Map.Entry<String, String> 类型的对象作为形参
    @Override
    public int compare(Map.Entry<String, String> item1, Map.Entry<String, String> item2) {
        //按照key值升序排列:String作为api提供的类,实现了Comparable的compareTo方法被设计成小于、等于、大于分别返回负数、零、正数
        return item1.getKey().compareTo(item2.getKey());

//        //按照key值降序排列
//        return item2.getKey().compareTo(item1.getKey());

//        //按照value值升序排列
//        return item1.getValue().compareTo(item2.getValue());

//        //按照value值降序排列
//        return item2.getValue().compareTo(item1.getValue());

    }
}

HashTable

和HashMap相似,HashTable的底层也是哈希表数据结构,线程是同步的(线程安全),但是不可以存入null值,效率较低。

package package03;

import java.util.Hashtable;

/*
HashTable

 */
public class HashTableTest {
    public static void main(String[] args) {
        Hashtable ht=new Hashtable();
        ht.put("张三",20);
//        ht.put("李四",null);//不能存null,会报NullPointerException
        ht.put("王五",21);

        System.out.println(ht);

        System.out.println(ht.get("李四"));
    }
}

集合的工具类

Connections工具类

操作名方法名方法描述
二分查找int binarySearch(list ,key);对list集合进行二分查找,前提是该集合必须有序,如果集合元素不是有序的,那么返回的结果是混乱的。
排序sort(list);对list集合进行排序,它其实就是封装了容器对于对象的compareTo方法
sort(list, comparator);根据指定的比较器进行排序
取大小max(Collection);取最大值
min(Collection);取最小值
反转reverse(list);对list集合进行反转
交换swap(list,x,y);对list集合中的元素进行位置的交换
替换replaceAll(list ,old,new);对list集合进行元素的替换,如要替换的元素不存在,则原集合不变
package package01;
import java.util.*;
/*
集合的工具类
Connections工具类

 * @Author WENG Jun
 * @Date 2021/7/17 - 9:36
 */
public class CollectionsTest {
    public static void main(String[] args) {
        List list = new ArrayList();

        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println("原始数据:" + list);

        //打乱list中的数据
        Collections.shuffle(list);
        System.out.println("打乱后的数据:" + list);

        //排序
        Collections.sort(list);
        System.out.println("排序后的结果:" + list);

        //二分查找
        System.out.println("二分查找20的索引:" + Collections.binarySearch(list, 20));//-11,查找不到
        System.out.println("二分查找5的索引:" + Collections.binarySearch(list, 5));//5


        //最大最小值
        System.out.println("最大值:" + Collections.max(list));
        System.out.println("最小值:" + Collections.min(list));

        //反转list
//        Collections.reverse(list);
//        System.out.println("反转后:"+list);

        //交换
        Collections.swap(list,1,2);
//        Collections.swap(list,1,100);//角标越界
        System.out.println("交换索引1和索引2位置的元素后:"+list);

        //替换
        Collections.replaceAll(list,0,999);
        System.out.println(Collections.replaceAll(list, 111, 000));//false,数据不存在,替换失败,不改变原有集合数据
        System.out.println(list);

    }
}

Arrays工具类

操作名方法名方法描述
二分查找binarySearch(数组类型);前提是该集合一定要是有序,如果需要查找的集合元素不是有序的,那么返回的结果是混乱的
数组排序sort(数组);对数组进行排序
转换toString(数组);将数组转换成字符串
复制数组copyOf(原数组,新数组长度);将需要的数组复制到另一个数组中
复制部分数组copyOfrange(原数组,起始索引,结束索引);复制所需的部分数组,从起始索引到结束索引,但是不包括结束索引
转变asList(T[]);将任意类型的数组转换成List集合,一旦通过此方法转换的集合,不能使用增删的方法,因为该长度是固定的,如果使用会出现UnsupportOperationExceptionl,但是可以使用contain和indexOf方法。
package package01;
import java.util.*;

public class ArraysTest {
    public static void main(String[] args) {

        int[] a = {2, 3, 16, 9, 1, 12};

        //Arrays.toString()
        System.out.println(Arrays.toString(a));//[2, 3, 16, 9, 1, 12]

        //Arrays.copyOf()
        int[] b = Arrays.copyOf(a, 4);
        System.out.println(Arrays.toString(b));//[2, 3, 16, 9]

        //Arrays.copyOfRange()
        int[] c = Arrays.copyOfRange(a, 1, 3);//从索引1到索引3,不包括3
        System.out.println(Arrays.toString(c));//[3, 16]

        //Arrays.asList()
        List<int[]> list = Arrays.asList(a);
        for (int[] ints : list) {
            System.out.println(ints);//[I@1540e19d
        }

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值