集合【Collection、ArrayList、LinkedList、Set、Map】

第十四章、集合

1.1、集合的理解和好处

1、集合

(1)可以动态保存任意多个对象,使用比较方便

(2)提供一系列方便的操作对象的方法:add、remove、set、get等

(3)使用集合添加,删除新元素的示意代码——更简介

1.2集合的框架体系图【背下来】

1、Collection

2、Map

]

集合主要分两组(单列集合、双列集合)

1、单列集合主要是只有一个对象

2、双列集合是一对键值对,存在key-value

3、Collection接口有两个重要的子接口

1.3、因为接口不能实现,只能用其子类来完成对接口的描述和实现

1.4、Collection(List和Set都有的接口)

Collection接口实现类的特点

1、collection实现可以存放多个元素,每个元素可以是Object

2、有些Collection的实现类,可以存放重复的元素,有些不可以

3、有些Collection的实现类,有些是有序的(List),有些是无序的(Set)

4、Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的

Collection接口的常用方法。

1、add:添加单个元素

(1)eg:

​ list.add("sadiao");
​ list.add(10); //list.add(new Integer(10)
​ // 因为添加的数据是对象

2、remove:删除指定元素

(1)eg:

​ remove删除
​ list.remove(0); //删除第一个元素
​ System.out.println(list);
​ list.remove("sadiao"); //指定删除某个数
​ System.out.println(list);

3、contains:查找某个元素是否存在 // 返回Boolean值

(1)eg:

​ 查找元素是否存在
​ System.out.println(list.contains(10));//T

4、size:获取元素个数

(1)eg:

​ 获取元素个数
​ System.out.println(list.size()); //1

5、isEmpty:判断是否为空

1)eg:

​ System.out.println(list.isEmpty()); //F

6、clear:清空

1)eg:

list.clear();

7、addAll:添加多个元素 //可以放集合进去

1)eg:

addall() 放集合进去
ArrayList list2 = new ArrayList();
list.add("sb");
list.add("ssss");
list.addAll(list2);

8、containsAll:查找多个元素是否都存在 //返回Boolean值

1)eg:

​ containsAll:查找多个元素是否存在,放集合进去
​ System.out.println(list.containsAll(list2));

9、removeAll:删除多个元素 //可以放集合进去

1)eg:

​ System.out.println(list.removeAll(list2));

Collection接口遍历元素方式1-使用Iteratior(迭代器)

1、Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。

2、所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,及可以返回一个迭代器。

3、Iterator的结构图

4、Iterator仅用于遍历集合,Iterator本身并不存放对象。

迭代器原理


1、注意:

(1)在调用iterator.next()方法之前必须要调用

(2)iterator.hasNext()进行检查。若不调用,且下一条记录无效,直接调用it.next()会抛出异常:

(3)NoSuchElementException异常

(4)当while循环后,iterator迭代器,指向最后一个元素,想要再次遍历,要重置迭代器。

2、eg:

Collection col  = new ArrayList();
    col.add(new Book("三国演义","罗贯中",1000));
    col.add(new Book("爱情买卖","二狗",1100));
    col.add(new Book("Java从入门到入土","身体日",888888));
    //        遍历col集合
        Iterator iterator = col.iterator();
//        使用while循环
        while(iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj="+obj);
        }
//        退出后,迭代器指向最后一行
//        重新迭代
        iterator = col.iterator();

Collection接口遍历对象方法2-for循环增强。

1、增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,本质一样。只能用于遍历结合或数组。

2、基本语法
(1)for(元素类型 元素名:集合名或数组名){

​ 访问元素

​ }

3、eg:

    for(Object book : col) {
        System.out.println("book = "+book);
    }
    int [] nums = {1,2,4,4,5,6};
    for(int i :nums) {
        System.out.println("i="+i);
    }

4、增强for循环,低层仍然是迭代器

1.5、List接口的方法

基本介绍:List接口是Collection接口的子接口

1、List集合类中元素是有序的(即添加顺序和取出顺序一致)、且可重复

(1)eg:

    List list = new ArrayList();
    list.add("aha1");
    list.add("Tom2");
    list.add("Fuc3");
    list.add("lululu4");
    list.add("lalala5");
    list.add("lalala5");
    System.out.println("list="+list);

2、List集合中的每一个元素都有其对应的顺序索引,支持索引

(1)eg:

​ System.out.println(list.get(3)); //lululu4且下标从0开始

3、JDK中API中List的接口实现类很多

(1)常用的有:ArrayList、LinkedList和Vector

List接口常用方法

1、void add(int index , Object ele):在index位置插入ele元素

2、Object get(int index):获取指定index位置的元素

3、int indexOf(Object obj):返回obj在集合中首次出现的位置

4、int lastIndexOf(Object obj ):返回obj在集合中末次出现的位置

5、Object remove(int index):移除指定位置的元素,并返回此元素

6、Object set(int index , Object ele):设置指定index位置的元素为ele,相当于替换

7、List subList(int formIndex ,int toIndex):返回从fromIndex到toIndex位置的子集合。下标前闭后开

8、eg:

    List list = new ArrayList();
    list.add("黄飞绿");
    list.add("大狗");
    list.add("二狗");
    list.add("三狗");
    list.add("四狗");

    list.add(2,"傻狗");
    System.out.println(list);
    //        addAll
        List list2 = new ArrayList();
        list2.add("五狗");
        list2.add("二狗");
        list.addAll(1,list2);
        System.out.println(list);
//        首次位置        
        System.out.println(list.indexOf("二狗"));//2
//        末次位置
        System.out.println(list.lastIndexOf("二狗"));  //5
//        移除
        System.out.println(list.remove(0));
//        替换
        System.out.println(list.set(1, "憨包"));
//        返回选中值
        List returnList = list.subList(0, 2);
        System.out.println(returnList);

普通for循环

// 3、使用普通for循环
for(int i=0;i<list.size();i++) {
System.out.println("对象="+list.get(i));
}

ArrayList注意事项

1、permits all elements, including null ,ArrayList可以加入null,并且多个

2、ArrayList是由数组来实现数据存储的

3、ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,在多线程情况下,不建议使用ArrayList

1.6、ArrayList底层结构和源码

1、ArrayList 中维护了一个Object类型的数组elementData.transient Object[] elementData;

2、当创建ArrayList对象时,如果使用的是无参构造器,则初始化elementData容量为0,第一次添加,则扩容到10,如需再次扩容,则扩容elementData为1.5倍。

3、如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。

1.7、Vector低层源码及剖析

基本介绍

1、Vector类的定义说明

2、Vector低层也是一个对象数组,protected Object [] elementData;

3、Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized

4、在开发中,需要线程安全时,考虑使用Vector

5、低层理解

(1)首先Vector在调用无参构造器时, Vector vec = new Vector();时是默认为10,传入

(2)调用add方法,添加数据到vec集合中

(3)确定是否需要扩容条件:minCapacity - elementData.length>0

(4)如果需要的数组大小,不够用,就扩容,

(5)扩容的算法:

Vector低层结构和ArrayList的比较

1.8、LinkedList

LinkedList的低层操作机制

1、LinkedList低层是维护了一个双向链表

2、LinkedList中维护了两个属性first和last分别指向首节点和尾节点

3、每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表

4、所有LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。

5、模拟一个简单的双向链表

6、老韩源码阅读

(1)添加


(2)删除:默认删除第一个节点

ArrayList和LinkedList比较

1、

如何选择ArrayList和LinkedList

1、如果我们改查的操作较多,选择ArrayList

2、如果我们增删的操作多,选择LinkedList

3、一般来说,大多数情况都是查询,因此大部分选择ArrayList

4、项目中,根据业务逻辑,一个模块使用的是ArrayList另一个模块选择的是LinkedList

1.9、Set接口和常用方法

基本介绍

1、无序(添加和取出顺序不一致),没有索引

2、不允许重复元素,所以最多包含一个null

3、JDK API中Set接口的实现类有

4、注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定。

Set常用方法和遍历方法

1、和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样

2、遍历

(1)迭代器

(2)增强for循环

    Set set = new HashSet();
    set.add("jack");
    set.add("tom");
    set.add("mary");
    set.add("qt");
    set.add("qt");
    set.add(null);
    set.add(null);
    for(int i=0;i<set.size();i++) {
        System.out.println("set="+set);
    }
//        使用迭代器
    Iterator iterator = set.iterator();
    while(iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj1="+obj);
    }
//        使用增强for循环
    for(Object object :set) {
        System.out.println("o="+object);
    }

1.10、Set接口实现类-HashSet

HashSet的全面说明

1、hasSet实现了Set接口

2、HashSet实际上是HashMap

3、可以存放null值,但是只能有一个null

4、HashSet不保证元素是有序的,取决于hash后,再确定索引的结果(即,不保证存放元素的顺序和取出 顺序一致)

5、不能重复元素/对象.在前面Set接口使用已经讲过

HashSet低层机制说明

1、HashSet低层是HashMap

2、添加一个元素时,先得到hash值-会转成->索引值

3、找到存储数据表table,看这个索引位置是否已经存放的有元素

4、如果没有,直接加入

5、如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后(程序员可以重写调用equals方法)

6、在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=Min_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

7、扩容机制


8、补充

(1)每加入一个结点Node,size++。而不是table增加才算

HashSet—实践

package set_;

import java.util.HashSet;
import java.util.Objects;

public class HashSetHomeword01 {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    /*
     * 定义一个Employee类,该类包含:private成员属性name,sal,birthday(MyDate类型)要求:
     * 其中birthday为MyDate类型(属性包括:year,month,
     * 创建3个Employee 对象放入HashSet中
     * 当name和age的值相同时,人为是相同员工,不能添加到HashSet集合中
     */
    HashSet hashSet = new HashSet();
    hashSet.add(new Employee1("傻瓜",5000,new MyDate(2022, 10, 5)));
    hashSet.add(new Employee1("傻瓜",5000,new MyDate(2022, 11, 25)));
    hashSet.add(new Employee1("黄瓜",5000,new MyDate(2022, 10, 5)));
    hashSet.add(new Employee1("憨包",5000,new MyDate(2022, 10, 5)));

    System.out.println(hashSet);
}
}
class Employee1{
    private String name;
    private int sal;
    private MyDate birthday;
    public Employee1(String name, int sal, MyDate birthday) {
        super();
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }
    @Override
    public String toString() {
        return "Employee1 [name=" + name + ", sal=" + sal + ", birthday=" + birthday + "]\n";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getSal() {
        return sal;
    }
    public void setSal(int sal) {
        this.sal = sal;
    }
    public MyDate getBirthday() {
        return birthday;
    }
    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }
    @Override
    public int hashCode() {
        return Objects.hash(birthday, name, sal);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee1 other = (Employee1) obj;
        return Objects.equals(birthday, other.birthday) && Objects.equals(name, other.name) && sal == other.sal;
    }
}

class MyDate{
    private int year;
    private int month;
    private int day;

public MyDate(int year, int month, int day) {
    this.year = year;
    this.month = month;
    this.day = day;
}
@Override
public String toString() {
    return "MyDate [year=" + year + ", month=" + month + ", day=" + day + "]";
}

public int getYear() {
    return year;
}
public void setYear(int year) {
    this.year = year;
}
public int getMonth() {
    return month;
}
public void setMonth(int month) {
    this.month = month;
}
public int getDay() {
    return day;
}
public void setDay(int day) {
    this.day = day;
}
@Override
public int hashCode() {
    return Objects.hash(day, month, year);
}
@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    MyDate other = (MyDate) obj;
    return day == other.day && month == other.month && year == other.year;
}
}

1.11、Set接口实现类LinkedHashSet

LinkedHashSet的全面说明

1、LinkedHashSet是HashSet的子类

2、LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表

3、LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

4、LinkedHashSet不允许添加重复元素

LinkedHashSetSource底层

2.1、Map接口和常用方法

2.1.1Map接口实现类的特点

1、Map于Collection并列存在。用于保存具有映射关系的数据:Key-Value

2、Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中

3、Map中的key不允许重复

4、Map中的value可以重复

5、Map的key可以为null。value也可以为null,注意key为null,只能有一个(主要原因还是key不能重复),value为null,可以有多个

6、常用String类作为Map的key

7、key 和value之间存在的单向一对一关系,即通过指定的key总能找到对应的value值

8、Map存放数据的key-value示意图,一堆k-v是存放在一个Node中的,有因为Node实现了 Entry接口,有些书上也说一堆k-v就是一个Entry

2.1.2Map接口常用方法

1、put()方法:

Map map = new HashMap();
map.put("邓超", "马蓉");

2、remove():删除映射关系

//        删除
        map.remove("宋喆");
        System.out.println("map="+map);

3、get():根据键获取值

(1)返回的是要用一个对象接受

//        获取键所对应的值
        Object val = map.get("王宝强");
        System.out.println("val="+val);
//结果是马蓉

4、size():获取键值对的尺寸

//        获取尺寸
        System.out.println("k-v="+map.size());

5、clear():清空键值对

6、containsKye():查看键是否存在返回boolean值

7.KeySet():获取所有的键

8、entrySet():获取所有关系k-v

9、values():获取所有的值

2.1.2、Map接口六大遍历方式

数据:

        Map map = new HashMap();
        map.put("邓超", "马蓉");
        map.put("王宝强", "马蓉");
        map.put("宋喆", "马蓉");
        map.put("刘凌波", null);
        map.put("邓超", "蓉");                            

1、第一种:先取出所有key,在用key取value

        Set keyset = map.keySet();
//        增强for循环
        for(Object obj :keyset) {
            System.out.println(obj+"——"+map.get(obj));
        }
//        迭代器
        Iterator iterator = keyset.iterator();
        while(iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println(obj+"——"+map.get(obj));
        }

2、第二种:把所有的values取出

        Collection values = map.values();
//        增强for循环
        for(Object obj :values) {
            System.out.println("obj="+obj);
        }
//        迭代器
        Iterator iterator2 = values.iterator();
        while(iterator2.hasNext()) {
            Object obj = iterator2.next();
            System.out.println("obj="+obj);
        }

3、第三种:通过EntrySet来获取k-v

(1)理解:就是HashMap$Node 实现了Map.Entry()的接口,就实现接口,调用该接口中的getKey()和getValue()方法

//        第三组:通过EntrySet来获取k-v
//        增强for循环
        Set entrySet = map.entrySet();
        for(Object entry:entrySet) {
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey()+"——"+m.getValue());
        }
//        迭代器
        Iterator iterator3 = entrySet.iterator();
        while(iterator3.hasNext()) {
            Object next = iterator3.next();
//            System.out.println(next.getClass());
////            向下转型
            Map.Entry m =(Map.Entry)next;
            System.out.println(m.getKey()+"———"+m.getValue());
        }

2.1.3、hashMap小结

1、Map接口的常用实现类:HashMap、Hashtable和Properties

2、HashMap是Map接口使用频率最高的实现类。[]()

3、HashMap是以key-val对的方式对存储数据(HashMap$Node类型)

4、key不能重复,但是是值可以重复,允许使用null见和null值

5、如果添加相同的key,则会覆盖原来的key-val,等同于修改.(key不会替换,val会替换)

6、与HahsSet一样,不保证映射 的顺序,因为低层是以hash表的方式来存储的。

7、HashMap没有实现同步,因此线程不安全,方法没有做同步互斥的操作,没有synchronized。

2.1.4、hashMap底层机制及源码剖析


1、扩容机制和HashSet相同

2、HashMap 底层维护了Node类型的数组table,默认为null

3、当创建对象时,加载因子(loadfactor)初始化为0.75

4、当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key是否加入的key相等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,作出相应处理。如果添加时发现容量不够,则需要扩容。

5、第一次添加,则需要扩容table容量为16,临界值(threshold)为12

6、以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,以此类推

7、在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化。

2.2、Map接口实现类—Hashtable

2.2.1、Hashtable基本介绍

1、存放的元素是键值对:即k-v

2、hashtable的键和值都不能为null,否则抛出NullPointerException

3、hashTable使用方法基本上和HashMap一样

4、hashTable是线程安全的,hashMap是线程不安全的

2.2.2、Hashtable底层扩容

1、执行方法addEntry(hash key ,value,index);添加k-v 封装到Entry

2、当if(count>=threshold)满足时,就进行扩容

3、按照int newCapacity=(oldCapacity<<1)+1;的大小扩容。

2.2.3、Hashtable和HashMap对比

image-20230322221223631

2.3、Map接口实现类—Properties

2.3.1、基本介绍

1、Properties类继承自Hashtable类且实现了Map接口,

也是使用一种键值对的形式来保存数据

2、使用特点和Hashtable类似

3、Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改。

4、说明:工作后xxx.properties文件通常作为配置文件,这个知识点在IO流中。

2.3.2、基本使用

//        基本使用
        Properties properties = new Properties();
//        增加
//        properties.put(null, "aaa");k-v不能为null
        properties.put("lucy",100);
        properties.put("lic", 100);
//        替换/修改
        properties.put("lic", 88);
        System.out.println(properties);

//        通过k获取v 查
        System.out.println(properties.get("lic"));//88
//        删除
        properties.remove("lic");

3、??总结开发中如何选择集合实现类(记住)

1、开发随机应变

3.1、先判断存储的类型

]

4、Collections工具类

4.1、Collections工具类的介绍

1、Collections是一个操作Set、List和Map等集合的工具类

2、Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

4.2、排序操作(均为static方法)

1、reverse(List):反转List中元素的顺序

2、shuffle(List):对List集合元素进行随机排序

3、sort(List):根据元素的自然顺序对指定List集合元素按升序排序

4、sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序

5、swap(List,int,int):将指定List集合中的i处元素和j出元素进行交换

6、Object max(Collection):根据元素的自然顺序给定集合中的最大元素

7、Object max(Collection,Comparator):根据Comparator指定的顺序, 返回给定集合中的最大元素

8、Object min(Collection)

9、Object min(Collection,Object):返回指定集合中指定元素的出现次数

10、int frequency(Collection,Object):返回指定集合何总指定元素的出现次数

11、void copy(List 的dest,List src):将src中的内容复制到dest中

12、boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值。

方法举例:

        List list = new ArrayList<>();
        list.add("tom");
        list.add("tom");
        list.add("smith");
        list.add("king");
        list.add("milanssss");
//        reverse(List):反转
        Collections.reverse(list);
        System.out.println("list="+list);
//        随机排序:shuffle()
        Collections.shuffle(list);
        System.out.println("list="+list);
//        自然排序:字符串排序
        Collections.sort(list);
        System.out.println("list="+list);
//        希望按照字符串的长度大小排序
        Collections.sort(list,new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                // TODO Auto-generated method stub
                return ((String)o1).length()-((String)o2).length();
            }

        });
        System.out.println("字符串长度大小排序="+list);
//        指定位置交换i和j交换
        Collections.swap(list, 0, 1);
        System.out.println("交换后="+list);

//        根据元素自然顺序
        System.out.println("集合元素最大值"+Collections.max(list));

//        根据Comparator比较器指定顺序,返回
//        比如,返回长度最大值
        Object maxObject =Collections.max(list,new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                // TODO Auto-generated method stub
                return ((String) o1).length() - ((String)o2).length();
            }
        });
        System.out.println("长度最大="+maxObject);

//        int frequency(Collection,Object):返回指定集合中指定元素的出现次数
        System.out.println("tom出现的次数="+Collections.frequency(list, "tom"));

//        void cpoy(List dest ,List src):将src中的内容复制到dest中
        ArrayList dest = new ArrayList();
//        需要先给dest复制,大小和list.size()一样
        for(int i=0 ;i<list.size();i++) {
            dest.add(" ");
        }
//        拷贝,
        Collections.copy(dest, list);
        System.out.println("dest= " +dest);
//        boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值。
//        如果list中有tom,就替换成傻瓜
        Collections.replaceAll(list, "tom", "傻瓜");
        System.out.println("list="+list);

5、总结

?

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇