Java集合总结

一、简介

Java集合主要由两个接口派生而来:Collection和Map。大致分为四个体系:

  • List:有序、可重复 (主要有ArrayList、LinkedList、Vector)
  • Set:无序、不可重复 (主要有HashSet、LinkedHashSet、TreeSet)
  • Map:有映射关系的集合 (主要有HashTable、HashMap、TreeMap、LinkedHashMap)
  • Queue:一种队列集合实现 (PriorityQueue)

结构图如下:
在这里插入图片描述
在这里插入图片描述

二、Collection接口

Collection接口是Set、List、Queue接口的父接口,该接口所有的方法可以供其子类调用实现。

1、Collection接口

1.1接口中的相关方法:

//添加方法:
add(Object o) //添加指定元素
addAll(Collection c) //添加指定集合

//删除方法:
remove(Object o) //删除指定元素
removeAll(Collection c) //输出两个集合的交集
retainAll(Collection c) //保留两个集合的交集
clear() //清空集合

//查询方法:
size() //集合中的有效元素个数
toArray() //将集合中的元素转换成Object类型数组

//判断方法:
isEmpty() //判断是否为空
equals(Object o) //判断是否与指定元素相同
contains(Object o) //判断是否包含指定元素
containsAll(Collection c) //判断是否包含指定集合

下面是一些常用方法的数据操作例子,主要是添加、删除、清空、是否为空等:

Collection c=   new ArrayList();
  //添加元素
  c.add("今天");
  c.add("明天");
  c.add(Integer.toString(8)); //基本数据类型需要转成包装类才能放入集合中
  System.out.println("c集合中的元素:"+c);
  //输出元素个数
  System.out.println("c集合中的元素个数为:"+c.size());
  //删除指定元素
  c.remove(Integer.toString(8));
  //再次输出集合中的元素个数
  System.out.println("c集合中的元素个数为:"+c.size());
  //判断是否包含指定对象
  System.out.println("c集合中是否包含\"今天\"字符串:"+c.contains("今天"));
  System.out.println("c集合中所有的元素:"+c);
Collection h= new HashSet();
  h.add("明天");
  h.add("明天的天气会下雨哦!");
  //判断c集合中是否完全包含h集合
  System.out.println("c集合中是否完全包含h集合?:"+c.containsAll(h));
  //用c集合减去h集合中的元素
  c.removeAll(h);
  System.out.println("c集合中的元素:"+c);
  //删除c中所有的元素
  c.clear();
  System.out.println("c集合中的元素:"+c);

输出结果:
在这里插入图片描述
1.2 使用Iterator(迭代器)遍历集合
Iterator接口是Collection接口的父接口,因此Collection集合可以直接调用其方法。
在这里插入图片描述
Iterator接口也是Java集合框架中的一员,与Collection系列和Map系列不同的是:
Collection系列、Map系列集合主要是用于盛装数据对象的。 Iteration主要是用于遍历Collection中的元素。
Iterator接口相关方法:

boolean hasNext();     //如果被迭代的集合元素还没有被遍历完,则返回true
Object next();         //返回集合里的下一个元素
void remove();                   //删除集合里上一次next方法返回的元素
void forEachRemaining(Consumer<? super E> action);  //Java8为Iterator新增的默认方法,可使用Lambda表达式来遍历集合元素。

下边通过Iterator接口类遍历集合元素

@Data
public class Person {
     String name;
      int age;
      public Person(String name, int age) {
          super();
          this.name = name;
          this.age = age;
      }
      @Override
      public String toString() {
          return "Person [name=" + name + ", age=" + age + "]";
      }
  }
@Test
public void testIterator(){
    // 创建集合添加元素
    Collection<Person> personList = new ArrayList<Person>();
    Person p1 = new Person("钟梅", 25);
    Person p2 = new Person("王兴", 34);
    personList.add(p1);
    personList.add(p2);
    // 获取集合的迭代器iterator
    Iterator<Person> iterator = personList.iterator();
    while (iterator.hasNext()) {
        // 获取集合中的下一个元素
        Person person = iterator.next();
        System.out.println("person:" + person.getName()+ "--" + person.getAge());

        if (person.getName().equals("王兴")) {
            // 删除通过next()获取的元素,在next()之后使用,不可以单独使用
            iterator.remove();
            /**
                 * 如果用集合本身的添加或删除方法进行修改集合元素,此时会报java.util.ConcurrentModificationException
                 * 如果要删除其中某个元素,只能用迭代器Interator的remove方法
                 */
           // ((ArrayList<Person>) personList).remove(person);
        }
        // 对person对象中的变量赋值,会改变集合中元素的值
        person.setName("马云");
        person.setAge(88);
    }
    System.out.println(personList.toString());
}

输出结果:
在这里插入图片描述
从上面输出结果可以看出,对迭代变量person对象进行赋值,当再次输出personList集合时,会看到集合中的元素发生了改变。由此可知,当使用Iteration对集合进行遍历迭代,会把集合元素值传递给迭代变量。当使用Iteration迭代变量Collection集合时,不可以在迭代过程中进行对集合添加、删除等操作,否则会引发java.util.ConcurrentModificationException异常,只能利用迭代器Iteration的remove方法进行删除上一次的next返回的元素。

2、Set集合

可以把多个对象存放入Set集合内,在集合内是无法记住元素的添加顺序,Set不允许包含重复元素,常用的有HashSet、TreeSet等
在这里插入图片描述

@Test
public void testSet(){
    Set<Person> set = new HashSet<Person>();
    Person p1 = new Person("钟梅", 25);
    //添加同一个对象两次
    boolean add1 = set.add(p1);
    boolean add2 = set.add(p1);

    System.out.println("add1:"+add1+"\nadd2:"+add2);
    System.out.println(set.toString());
}

输出结果:
在这里插入图片描述
从结果上可以,添加同一对象两次到集合中,第二次会操作失败add返回false,由此可见Set集合中不能存在相同的对象。接下来可以测试下添加有相同的元素值的两个对象看看结果是怎么样的:

@Test
public void testSet(){
    Set<Person> set = new HashSet<Person>();
    Person p1 = new Person("钟梅", 25);
    Person p2 = new Person("钟梅", 25);
    boolean add1 = set.add(p1);
    boolean add2 = set.add(p2);

    System.out.println("add1:"+add1+"\nadd2:"+add2);
    System.out.println(set.toString());
}

输出结果:
在这里插入图片描述
由此可见:添加相同元素是指同一对象引用被多次添加会操作失败,而非相同元素值的多个对象被添加。

3、List集合

3.1简介:
List代表是一个元素有序、可重复的集合,集合中每个元素都有对应的顺序索引。List集合允许使用重复的元素,可以通过索引来访问指定位置的元素。List集合默认按元素的添加顺序来设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为0,依次类推下去。主要有ArrayList、LinkedList、 Vector、stack
在这里插入图片描述
3.2:接口中定义的方法
List作为Collection的子接口,同样可以调用Collection的全部方法,List集合具有有序特点,同时List集合还有一些额外的方法:

void add(int index, Object element) //在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<!--? extends E--> c) //将集合c 中的所有元素都插入到列表中的指定位置index处。
Object get(index) //返回列表中指定位置的元素。
int indexOf(Object o)  //返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o)  //返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
Object remove(int index)  //移除列表中指定位置的元素。
Object set(int index, Object element)  //用指定元素替换列表中指定位置的元素。
List subList(int fromIndex, int toIndex)  //返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的所有集合元素组成的子集。
Object[] toArray()  //返回按适当顺序包含列表中的所有元素的数组(从第一个元素到最后一个元素)。

除此之外,Java 8还为List接口添加了如下两个默认方法:

void replaceAll(UnaryOperator operator)  //根据operator指定的计算规则重新设置List集合的所有元素。
void sort(Comparator c)  //根据Comparator参数对List集合的元素排序。

测试代码:

@Test
public void testList(){
    //创建List集合,初始化数据
    List<Person> list = new ArrayList<Person>();
    list.add(new Person("马云", 88));
    list.add(new Person("马化腾", 44));
    list.add(new Person("任正非", 99));
    System.out.println(list.toString());
    System.out.println("------------------------------------");
    //在第二位置插入新数据
    Person p1 = new Person("李彦宏", 66);
    list.add(1, p1);
    //普通变量集合for
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
    System.out.println("------------------------------------");
    //删除第二个元素
    list.remove(2);
    System.out.println(list.toString());
    System.out.println("------------------------------------");
    //获取指定元素在集合中的位置
    Person p2 = new Person("李彦宏", 66);
    //返回-1,表明该对象不存在
    System.out.println(list.indexOf(p2));
    System.out.println("------------------------------------");
    //替换指定索引的元素
    list.set(1,new Person("刘强东", 77));
    System.out.println(list.toString());
    System.out.println("------------------------------------");
    //截取指定位置区域的元素成为子集合(注意是位置,不是索引值)
    System.out.println(list.subList(1, 2));
}

测试结果:
在这里插入图片描述
从结果可以当执行System.out.println(list.indexOf(p2))并没有返回指定的对象的索引值,返回是-1,说明集合不存在该元素,控制台打印的是p1
3.3 ArrayList和LinkedList的区别是什么?
数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标
3.4 ArrayList 和 Vector 的区别是什么?
线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
性能:ArrayList 在性能方面要优于 Vector。
扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

4、Queue集合

4.1:简介
Queue用于模拟队列这种数据结构,队列通常是指“先进先出”(FIFO)的容器。队列的头部保存在队列中,存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。
4.2:接口中定义的方法

boolean add(E e)
将指定的元素插入到此队列中,如果可以立即执行此操作,而不会违反容量限制, true在成功后返回 IllegalStateException如果当前没有可用空间,则抛出IllegalStateException。

E element()
检索,但不删除,这个队列的头。

boolean offer(E e)
如果在不违反容量限制的情况下立即执行,则将指定的元素插入到此队列中。

E peek()
检索但不删除此队列的头,如果此队列为空,则返回 nullE poll()
检索并删除此队列的头,如果此队列为空,则返回 nullE remove()
检索并删除此队列的头。

三、Map集合

1.1 简介
Map保存具有映射关系的数据,由key和value组成,key和value是一一对应的, 即通过指定的key,总能找到唯一的、确定的value
在这里插入图片描述
1.2:Map集合与Set集合、List集合的关系
与Set集合的关系
如果 把Map里的所有key放在一起看,它们就组成了一个Set集合(所有的key没有顺序,key与key之间不能重复),实际上Map确实包含了一个keySet()方法,用户返回Map里所有key组成的Set集合。
与List集合的关系
如果把Map里的所有value放在一起来看,它们又非常类似于一个List:元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中索引不再使用整数值,而是以另外一个对象作为索引。

1.3:常用接口定义的方法

void clear()
从该地图中删除所有的映射(可选操作)。

boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回 true 。
boolean containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回 true 。
Set<map.entry<k,v>> entrySet()
返回此地图中包含的映射的Set视图。
V get(Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()
如果此地图不包含键值映射,则返回 true 。
Set<k> keySet()
返回此地图中包含的键的Set视图。
V put(K key, V value)
将指定的值与该映射中的指定键相关联(可选操作)。
void putAll(Map<!--? extends K,? extends V--> m)
将指定地图的所有映射复制到此映射(可选操作)。
V remove(Object key)
如果存在(从可选的操作),从该地图中删除一个键的映射。
default boolean remove(Object key, Object value)
仅当指定的密钥当前映射到指定的值时删除该条目。
default V replace(K key, V value)
只有当目标映射到某个值时,才能替换指定键的条目。
default boolean replace(K key, V oldValue, V newValue)
仅当当前映射到指定的值时,才能替换指定键的条目。
int size()
返回此地图中键值映射的数量。
Collection<v> values()
返回此地图中包含的值的Collection视图。

Map中还包括一个内部类Entry,该类封装了一个key-value对。Entry包含如下三个方法:

K getKey()
返回与此条目相对应的键。
V getValue()
返回与此条目相对应的值。
V setValue(V value)

测试方法:

@Test
public void testMap(){
    Map map = new HashMap();
    //以key-value对放入map中
    map.put("马云", 100);
    map.put("马化腾", 250);
    map.put("李彦宏", 88);
    //value值是可以重复
    map.put("任正非", 100);
    //如果key重复了,value返回值被覆盖掉的value,也就是还是之前的value
    System.out.println("马化腾:"+map.put("马化腾", 100));
    System.out.println("-------------------------------------");
    //判断集合中是否指定的key和value
    System.out.println("key中是否有\"马云\" :"+map.containsKey("马云"));
    System.out.println("-------------------------------------");
    System.out.println("value中是否有\"340\" :"+map.containsValue(340));
    System.out.println("-------------------------------------");
    //遍历map
    for (Object key:map.keySet()) {
        System.out.println("key:"+key+"--value:"+map.get(key));
    }
    System.out.println("-------------------------------------");
    //移除指定的的key-value元素
    map.remove("马化腾");
    System.out.println(map.toString());
}

输出结果:
在这里插入图片描述
遍历Map的方法:

@Test
public void traverseMap(){
    Map<String, String> map = new HashMap<String, String>();
    map.put("1", "value1");
    map.put("2", "value2");
    map.put("3", "value3");
    //第一种:常规  通过Map.keySet遍历key和value
    for (String key:map.keySet()) {
        System.out.println("key= "+ key + " and value= " + map.get(key));
    }
    System.out.println("----------");
    //第二种:通过key值的集合Set,再通过迭代器遍历;
    Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Entry<String, String> entry = iterator.next();
        System.out.println("key= "+ entry.getKey() + " and value= " + entry.getValue());
    }
    System.out.println("----------");
    //第三种
    Set<Entry<String, String>> entrySet = map.entrySet();
    for (Entry<String, String> entry:entrySet) {
        System.out.println("key= "+ entry.getKey() + " and value= " + entry.getValue());
    }
    System.out.println("----------");
    //第四种:通过Map.values()遍历所有的value,但不能遍历key
    for (String value:map.values()) {
        System.out.println("value= " + value);
    }
}

四、总结

4.1 List 有序,不唯一

* ArrayList

优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高

* Vector

优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程安全,效率低

* LinkedList

优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
4.2 Set 无序,唯一

* HashSet

底层数据结构是哈希表。(无序,唯一)
如何来保证元素唯一性?
依赖两个方法:hashCode()和equals()

* LinkedHashSet

底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一

* TreeSet

底层数据结构是红黑树。(唯一,有序)

  • 如何保证元素排序的呢?
    自然排序
    定制排序
  • 如何保证元素唯一性的呢?
    根据比较的返回值是否是0来决定

TreeSet,、LinkedHashSet 、HashSet 的区别

  • TreeSet的主要功能用于排序
  • LinkedHashSet的主要功能用于保证FIFO即有序的集合(先进先出)
  • HashSet只是通用的存储数据的集合
  • HashSet不保证有序,LinkHashSet保证FIFO即按插入顺序排序,TreeSet安装内部实现排序,也可以自定义排序规则
  • null:HashSet和LinkHashSet允许存在null数据,但是TreeSet中插入null数据时会报NullPointerException

HashMap、TreeMap、HashTable的区别

  • TreeMap是有序的,HashMap和HashTable是无序的。
  • Hashtable的方法是同步的,线程安全;HashMap的方法不是同步的,线程不安全。
  • Hashtable效率较低,HashMap效率较高。
  • 如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。
  • Hashtable不允许null值,HashMap允许null值(key和value都允许)
  • 父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap

参考:
https://www.2cto.com/kf/201805/746424.html
https://blog.csdn.net/iruier_/article/details/80010308
https://blog.csdn.net/zhangqunshuai/article/details/80660974

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值