一文看懂java集合(图文详细)

java集合框架图

看图可知,主要分为两类:Collection 和 Map,Collection主要用于存储一组对象,Map用于存储键-值对。

对这二者再细分

Collection接口:

Map接口 

 

 集合框架总结

一、Collection 接口的接口 对象的集合(单列集合)

  1. List 接口:元素按进入先后有序保存,有序,可重复
    1. LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
    2. ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
    3. Vector 接口实现类 数组, 同步, 线程安全
  2. Set 接口: 仅接收一次,无序,不可重复,并做内部排序
    1. HashSet 使用hash表(数组)存储元素
      1. LinkedHashSet 链表维护元素的插入次序
    2. TreeSet 底层实现为二叉树,元素排好序

二、Map 接口 键值对的集合 (双列集合)

  1. Hashtable 接口实现类, 同步, 线程安全
  2. HashMap 接口实现类 ,没有同步, 线程不安全
    1. LinkedHashMap 有序
  3. TreeMap 红黑树对所有的key进行排序

一、Collection接口

//因为Collection是接口,不能实例化,所以我们用他的一个实现类ArrayList来演示
List list = new ArrayList<>();
list.add("mk");
list.add(true);
list.add(12.3);

也可以存储指定类型的数据 

List<String> list = new ArrayList<>();
list.add("mk");

 常用方法

int size();

获取集合大小

boolean isEmpty();

判断是否为空

boolean contains(Object var1);

判断是否包含

Object[] toArray();

将集合转换为数组

boolean add(E var1);

添加元素

boolean addAll(Collection var1);

添加一个集合的全部元素

boolean remove(Object var1);

删除集合中某个元素

boolean remove(int index);

删除集合中指定下标的元素

boolean removeAll(Collection var1);

删除一个集合的全部元素

void clear();

清空集合

boolean containsAll(Collection var1);

判断是否包含另一个集合的元素

boolean equals(Object var1);

判断是否相同

default Spliterator spliterator();

得到该集合的分离器

default Stream stream();

转换为Stream流

default Stream parallelStream();

转换为Stream并行流

测试

 
public class Test {
    public static void main(String[] args) {
        //定义一个集合并向上转型
        Collection<String> obj = new ArrayList<>();
        //1.int size();    获取集合大小
        int size = obj.size();
        //2.boolean isEmpty();    判断是否为空
        boolean empty = obj.isEmpty();
        //3.boolean contains(Object var1);    判断是否包含
        boolean contains = obj.contains(null);
        //4.Object[] toArray();    将集合转换为数组
        Object[] objects = obj.toArray();
        //5.boolean add(E var1);    向集合增加元素
        boolean result = obj.add("Hello World");
        //6.boolean remove(Object var1);    向集合移除元素
        boolean remove = obj.remove(null);
        //7.boolean containsAll(Collection<?> var1);    判断是否包含另一个集合的元素
        boolean containsResult = obj.containsAll(new ArrayList<>());
        //8.boolean addAll(Collection<? extends E> var1);    添加一个集合的全部元素
        boolean addAllResult = obj.addAll(new ArrayList<>());
        //9.boolean removeAll(Collection<?> var1);    删除一个集合的全部元素
        boolean removeAllResult = obj.removeAll(new ArrayList<>());
        //10.void clear();    清空集合
        obj.clear();
        //11.boolean equals(Object var1);    判断是否相同
        boolean equalsResult = obj.equals(null);
        //12.default Spliterator<E> spliterator();    得到该集合的分离器
        Spliterator<String> spliterator = obj.spliterator();
        //13.default Stream<E> stream();    转换为Stream流
        Stream<String> stream = obj.stream();
        //14.default Stream<E> parallelStream();    转换为Stream并行流
        Stream<String> stringStream = obj.parallelStream();
    }
}

遍历方法

两种遍历方法:

  1. Iterator迭代器
  2. 增强for循环(foreach循环)

方法1:Iterator迭代器

        //1. 先得到collection对应的迭代器
        Iterator iterator = collection.iterator();
        //2. 开始遍历
            //判断是否还有下一个元素
        while (iterator.hasNext()) {
            //返回下一个元素
            Object next =  iterator.next();
            System.out.println(next);
            
        }
        //重置iterator
        iterator = collection.iterator();

作用

相当于一个指针,用于遍历一个集合

1.为什么需要迭代器?

在实际应用中经常需要将容器遍历判断满足业务需求。

2.如何实现迭代?

Iterator类中有 hasNext()和next() 两个方法,因为遍历过程首先得判断是否为空,不为空才能继续遍历。

Iterator类具有remove()方法,因为Collection类中的removeIf()方法是jdk1.8后才有的,所以需要满足能有通过逻辑删除的需求。

3.如果希望再次使用一个迭代器,需要重置

因为使用完一次迭代器之后,指针会指向最后一个元素,我们想再次使用,就得将其重置,否则会报错

4.生成代码的快捷键

itit

示例


public class Test {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add(new Book("三国演义","罗贯中",10.1));
        collection.add(new Book("小李飞刀","古龙",5.1));
        collection.add(new Book("海贼王","尾田",34.6));

        //1. 先得到collection对应的迭代器
        Iterator iterator = collection.iterator();
        //2. 开始遍历
            //判断是否还有下一个元素
        while (iterator.hasNext()) {
            //返回下一个元素
            Object next =  iterator.next();
            System.out.println(next);
            
        }
        //重置iterator
        iterator = collection.iterator();
    }

}

class Book {
    String name;
    String author;
    double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

方法2:foreach循环

        for (Object object : collection) {
            System.out.println(object);
        }

增强for循环,就是foreach循环,可以代替iterator迭代器

特点:增强for就是简化版的iterator,本质一样,它的底层也是 Iterator。数组和集合都能用。

快捷键:

collection.for 

示例

public class Test {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add(new Book("三国演义","罗贯中",10.1));
        collection.add(new Book("小李飞刀","古龙",5.1));
        collection.add(new Book("海贼王","尾田",34.6));

        for (Object object : collection) {
            System.out.println(object);
        }
    }

}

class Book {
    String name;
    String author;
    double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

Collection集合适用场景分析

一.1、List接口

Collection接口的方法,List接口和Set接口都有,但是List接口和Set接口他们自己的方法,对方是没有的。

特点

  1. List集合类中元素有序(即添加顺序和取出顺序一致)
  2. 可重复
  3. List集合中的每个元素嘟有其对应的顺序索引,即支持索引

        也就是可以 用 list.get(3) 来拿到第4个元素

常用方法

除去Collection接口的方法,List接口还有自己独特的方法

因为List接口是有序的,所以它可以用很多带有索引的方法

package edu.tjdz.javaSE.collection;
 
import java.util.*;
 
/*
测试List接口中的常用方法
    1、List集合存储元素特点:有序可重复
        有序:List集合中元素由下标。
          从0开始,以1递增。
        可重复:存储一个1,还可以在存储一个1.
    2、List即是Collection接口中的子接口,那么肯定List接口有自己“特色”的方法:
        以下只列出List特有的常用的方法
        void add(int index, E element)  在列表的指定位置添加元素(第一个参数是下标)
        Object get(int index)     根据下标获取元素(元素的下标,从0开始,以1递增)
        int indexOf(Object o)   //获取指定对象第一次出现的索引
        int lastIndexOf(Object o)     //获取指定对象最后一次出现的索引
        Object remove(int index)      //删除指定下标位置的元素
        Object set(int index, Object element)   //修改指定位置元素(元素下标,修改的值)
 */
public class CollectionTest07 {
    public static void main(String[] args) {
        //创建List集合
        //List myList = new LinkedList();
        //List myList = new Vector();
        List myList = new ArrayList();
 
        //添加元素
        myList.add("A");  //默认都是向集合尾部添加元素
        myList.add("B");
        myList.add("C");
        myList.add("D");
        myList.add("A");
 
        //在列表的指定位置添加元素(第一个参数是下标)
        //这个方法使用不多,因为对于ArrayList集合来说,效率较低(因为ArrayLIst底层是数组,添加的时候会涉及到元素位移的问题)
        myList.add(1,"KING");
 
        //迭代
        Iterator it = myList.iterator();
        while(it.hasNext()){
            Object elt = it.next();
            System.out.println(elt);
        }
 
        System.out.println("------------------------------");
 
        //根据下标
        Object firstobj = myList.get(0);
        System.out.println(firstobj);  //A
 
        //因为有下标,所以List集合有自己特殊的遍历方式
        //根据下标遍历【List集合特有的方式,Set没有。】
        for(int i=0;i<myList.size();i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }
 
        //获取指定对象第一次出现的索引
        System.out.println(myList.indexOf("KING"));  //1
 
        //获取指定独享最后一次出现处的索引
        System.out.println(myList.lastIndexOf("A")); //5
 
        //删除指定下标位置的元素
        myList.remove(0);
        System.out.println(myList.size()); //5
 
        System.out.println("================================");
        //修改指定位置元素
        myList.set(0,"Soft");
 
        //索引方式遍历集合
        for(int i=0;i<myList.size();i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }
    }
}

三种遍历方法

List可以用collection接口的两种方法:Iterator迭代器、foreach循环

还能有一种自己独特的方法,因为List接口是有序的,所以可以用索引的方式来遍历

        //遍历集合
        for(int i=0;i<myList.size();i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }

ArrayList、Vector、LinkedList区别

底层

查询

增删

线程安全

效率

扩容倍数

ArrayList

数组

不安全

如果有参构造,默认大小:参数,满后按照1.5倍扩容

如果无参构造,默认大小:0,第一次容量扩大到10,从第二次开始1.5倍扩容

Vector

数组

安全

如果有参构造,默认大小:参数,满后每次扩大两倍

如果无参构造,默认大小:10,满后就按照2倍扩容

LinkedList

链表

不安全

链表,不用扩容

ArrayList

 扩容机制

  1. ArrayList中维护了一个Object类型的数组elementData.
  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
  3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
  4. 扩容方法有两种,add 和 addAll方法

2和3 分别就是下图中的两个

 

 

注意:代码中的右移就是÷2的意思

Vector

(1)Vector可以实现可增长的对象数组。与数组一样,可以使用整数索引进行访问的组件。不过,Vector的大小是可以增加或者减小的,以便适应创建Vector后进行添加或者删除操作。

(2)同时Vector是线程安全的!底层使用的是synchronized进行加锁。

扩容机制

如果有参构造,默认大小:参数,满后每次扩大两倍

如果无参构造,默认大小:10,满后就按照2倍扩容

LinkedList

底层机制

  1. LinkedList底层维护了一个双向链表.
  2. LinkedList中维护了两个属性first和last分别指向首节点和尾节
  3. 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
  4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。

一.2、Set接口 

特点

  1. 无序(添加和取出的顺序不一致)
  2. 不允许重复元素,所以最多包含一个null
  3. 没有索引,也就是不能用索引来取出数据

常用方法

除去Collection接口的方法,Set接口也有自己独特的方法

两种遍历方法

也就是Collection接口的两种遍历方法:Iterator迭代器、foreach循环

因为Set是无序的,所以不能使用索引的方式来遍历

面试题:Set不能添加重复的元素?

public class SetTest {
    public static void main(String[] args) {
        Set set = new HashSet();
        // Hashset不能添加相同的元素/数据?
        set.add("lucy");//ok
        set.add( "lucy");//no

        set.add(new Dog("tom" ));//OK
        set.add(new Dog( "tom"));//0k


        //再加深一下。非常经典的面试题。
        set.add(new String("hsp"));//ok
        set.add(new String("hsp"));//no

        System.out.println( "set=" + set);
    }
}

class Dog{
    String name;
    Dog(String name){
        this.name = name;
    }
}

输出

set=[hsp, 集合.Dog@4554617c, 集合.Dog@1b6d3586, lucy]

lucy那里,因为地址一样,都是常量池中的,所以是重复元素,所以第二个不能加入。

Dog那里,因为两个dog 都是新的对象,地址不一样,所以不算重复的元素,所以可以加入。

那第三对 是为什么?

HashSet

Java中哈希集(HashSet)概念,实现以及操作_java hashset_Sueko的博客-CSDN博客

  1. HashSet底层是HashMap, HashMap底层是(数组+链表+红黑树)
  2. HashSet是基于HashMap来实现的,实现了Set接口,同时还实现了序列化和可克隆化。而集合(Set)是不允许重复值的。
  3. 所以HashSet是一个没有重复元素的集合,但不保证集合的迭代顺序,所以随着时间元素的顺序可能会改变。
  4. 由于HashSet是基于HashMap来实现的,所以允许空值,不是线程安全的

LinkedHashSet

  1. LinkedHashSet 是 HashSet的子类
  2. LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
  3. LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。
  4. LinkedHashSet 不允许添重复元素

二、Map接口

特点

注意:这里讲的是JDK8的Map接口特点

  1. Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
  2. Map 中的 key和 value可以是任何引用类型的数据,会封装到HashMap$Node·对象中
  3. Map 中的key 不允许重复、Map 中的value 可以重复
  4. Map 的key可以为null, value也可以为null,注意key 为null, 只能有一个,value 为null ,可以多个.
  5. 常用String类作为Map的key
  6. key和 value之间存在单向一对一关系,即通过指定的key 总能找到对应的value
  7. 一个k-v 就是一个 Entry

常用方法

添加、删除、修改操作:

Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中

void putAll(Map m):将m中的所有key-value对存放到当前map中

Object remove(Object key):移除指定key的key-value对,并返回value

void clear():清空当前map中的所有数据

元素查询的操作:

Object get(Object key):获取指定key对应的value

boolean containsKey(Object key):是否包含指定的key

boolean containsValue(Object value):是否包含指定的value

int size():返回map中key-value对的个数

boolean isEmpty():判断当前map是否为空

boolean equals(Object obj):判断当前map和参数对象obj是否相等

元视图操作的方法:

Set keySet():返回所有key构成的Set集合

Collection values():返回所有value构成的Collection集合

Set entrySet():返回所有key-value对构成的Set集合

常用实现类

Map 接口 键值对的集合 (双列集合)

  1. Hashtable 接口实现类, 同步, 线程安全
  2. HashMap 接口实现类 ,没有同步, 线程不安全
  3. TreeMap 红黑树对所有的key进行排序

Map接口的常用实现类:HashMap(子类LinkedHashMap)、TreeMap和Hashtable(子类Properties).

遍历方法

第一种:用map.KeySet()方法拿到key

先用map.KeySet()方法拿到所有的key,将这些key存到一个Set集合中,再遍历这些key,用map.get(key)方法拿到value

可以用Set集合遍历的foreach循环遍历key,也可以用Set集合的迭代器key

注意:不推荐这种方法,因为只能获取key,要想获取对应的value,需要重复计算,效率低

public static void main(String[] args) {
    Map map = new  HashMap();
    map.put("张三","李四");
    map.put("路飞","女帝");
    map.put("鸣人","雏田");
    map.put("佐助","小樱");
    map.put("邓超","孙俪");


    System.out.println("第一种:用for循环--------------");
    for(Object key : map.keySet()){
        System.out.println(key + "-" + map.get(key));
    }
    
    
    System.out.println("第二种:用迭代器--------------");
    Iterator iterator = map.keySet().iterator();
    while (iterator.hasNext()) {
        Object key =  iterator.next();    
        System.out.println(key + "-" + map.get(key));
    }

}

第二种:用map.values方法取出所有的value

用map.values() 方法拿到所有的value,存到一个collection中,在用collection接口的两种遍历方法去遍历

注意:这种方法只能取出value,不能取出key,不推荐

public static void main(String[] args) {
    Map map = new  HashMap();
    map.put("张三","李四");
    map.put("路飞","女帝");
    map.put("鸣人","雏田");
    map.put("佐助","小樱");
    map.put("邓超","孙俪");

    Collection values = map.values();
    System.out.println("第一种:用for循环--------------");
    for(Object value : values){
        System.out.println(value);
    }
    System.out.println("第二种:用迭代器--------------");
    Iterator iterator = values.iterator();
    while (iterator.hasNext()) {
        Object value =  iterator.next();
        System.out.println(value);
    }

}

 第三种:用map.entrySet()方法取出k-y

首先通过map.entrySet()方法,可以获取到一个Set集合,这个集合中的每一个元素就是Map中的一个键值对。然后通过循环遍历这个Set集合,可以依次取出每对的键和值。该方法使用了foreach循环,代码简洁明了,且能获取Map的键和值,是最常见且多数情况最可取的遍历方式。

注意:推荐使用这种方式,效率高

public static void main(String[] args) {
    Map map = new  HashMap();
    map.put("张三","李四");
    map.put("路飞","女帝");
    map.put("鸣人","雏田");
    map.put("佐助","小樱");
    map.put("邓超","孙俪");

    Set entrySet = map.entrySet();
    System.out.println("第一种:用for循环--------------");
    for(Object entry : entrySet){
        //将entry 转换成 Map.Entry
        Map.Entry m =(Map.Entry) entry;
        System.out.println(m.getKey() + "-" + m.getValue());
    }

    System.out.println("第二种:用迭代器--------------");
    Iterator iterator = entrySet.iterator();
    while (iterator.hasNext()) {
        Object entry =  iterator.next();
        //将entry 转换成 Map.Entry
        Map.Entry m =(Map.Entry) entry;
        System.out.println(m.getKey() + "-" + m.getValue());
    }

}

猜你喜欢

转载自blog.csdn.net/KangYouWei6/article/details/132561298