java集合

一、集合与数组

1、 集合与数组存储数据概述

  集合、数组都是对多个数据进行存储操作的结构,简称Java容器。 说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)。

2、数组存储的特点

  一旦初始化以后,其长度就确定了。 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
比如:

String[] arr0;  int[] arr1;  Object[] arr2;

3、数组存储的弊端

  1. 一旦初始化以后,其长度就不可修改。
  2. 数组中提供的方法非常限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
  3. 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
  4. 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

4、集合存储的优点

  解决数组存储数据方面的弊端。

5、集合的分类

  Java集合可分为Collection和Map两种体系

  1. Collection接口:单列数据,定义了存取一组对象的方法的集合。
  2. Map接口:双列数据,保存具有映射关系“key-value对”的集合。

二、Collection接口

1、简介

  1. JDK不提供Collection接口的任何直接实现,而是提供更具体的子接口实现类(如:Set和List)。
  2. Collection接口是List、Set、Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List集合和 Queue集合。
    在这里插入图片描述

2、Collection接口常用方法

01add(Object obj);						--添加一个元素
02addAll(Collection coll);				--添加一个集合
03int size();							--获取有效元素的个数
04void clear();						--清空集合
05boolean isEmpty();					--判断是否为空,集合是否包含某个元素
06boolean contains(Object obj);		--判断是否包含某一个元素(调用元素的equals方法来比较的)
07boolean containsAll(Collection c);	--判断是否包含某一个集合元素(也是调用元素的equals方法来比较的。 )
08boolean remove(Object obj);			--删除一个元素;通过元素的equals方法判断是否是要删除的元素。只会删除找到的第一个元素。
09boolean removeAll(Collection coll);	--删除该集合中与coll中相同得值,
10boolean retainAll(Collection c);	--把当前集合中的元素和c集合中相同的元素获取出来重新赋值给该集合,不影响c集合。
11boolean equals(Object obj);			--判断集合是否相等。
12Object [] toArray();				--转换成对象数组。
13int hashCode();						--获取集合对象的哈希值。
14Iterator<E> iterator();				--返回迭代器对象,用于集合遍历。

3、Collection集合与数组间的转换

  1. 集合转数组
public static void main(String[] args) {
    Collection collection = new ArrayList();
    collection.toArray();
}
  1. 数组转集合
public static void main(String[] args) {
    String[] s = {"AA", "BB", "CC"};
    List<String> list = Arrays.asList(s);
}

4、遍历集合

4.1、遍历Collection的3种方式

  1. 迭代器Iterator
  2. forEach循环(或增强for循环)
  3. Stream流

4.2、案例

  1. 迭代器Iterator :主要用于遍历 Collection 集合中的元素。 GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生
public static void main(String[] args) {
    Collection<String> list = Arrays.asList();
    Iterator<String> iterator = list.iterator();
    //hasNext():判断是否还下一个元素
    while (iterator.hasNext()) {
        //next():①指针下移 ②下移以后,将集合位置上的元素返回
        System.out.println(iterator.next());
    }
}
  1. forEach循环(或增强for循环)
public static void main(String[] args) {
    //1.forEach
    Collection<String> list = Arrays.asList();
    list.forEach(s-> System.out.println());
    
    //2.for
    for (String s : list) {
        System.out.println(s);
    }
}
  1. Stream流
public static void main(String[] args) {
    Collection<String> list = Arrays.asList();
    list.stream().forEach(l -> System.out.println(l));
}

4.3、iterator中remove()方法的使用

  1. 内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove();
  2. 在未调用next()或者在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。

示例

    public static void main(String[] args) {
        Collection<String> list = Arrays.asList("1", "2", "3");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            // iterator.remove();//1、在未调用next()前调用会报错
            Object obj = iterator.next();
            if ("Tom".equals(obj)) {
                iterator.remove();
                // iterator.remove();//2、连续调用两次也会报错。
            }
        }
    }

5、Collection子接口:List接口

5.1、存储的数据特点

  1. 存储有序的、可重复的数据。
  2. 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组。
  3. List容器中的元素都对应一个整数型的序号索引,记载其在容器中的位置,可以根据序号存取容器中的元素。
  4. JDK AP中List接口的实现类常用的有:ArrayList、LinkedList和 Vector。

5.2、常用实现类

5.2.1、ArrayList

  作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储,默认情况下,扩容为原来的1.5倍,同时将原有的数组数据复制到新的数组中。ArrayList是List接口的典型实现类、主要实现类,用的也是最多的。可以存储null值。
问:Array Listi的JDK 1.8之前与之后的实现区别?
  JDK 1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组。
  JDK 1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组。

5.2.2、LinkedList

  对于频繁的插入、删除操作,使用此类效率比ArrayList高;因为底层使用双向链表存储。双向链表,内部没有声明数组,而是定义了Node类型的 frstlast,用于记录首末元素。同时,定义内部类Node,作为 Linkedlist中保存数据的基本结构。Node除了保存数据,还定义了两个变量 :prev 变量记录前一个元素的位置, next 变量记录下一个元素的位置。
在这里插入图片描述

5.2.2、Vector

• Vector是一个古老的集合,JDK 1.0就有了。大多数操作与ArrayList相同,区别在于Vector是线程安全的。
• 在各种List中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免选择使用。
• JDK 7.0和JDK 8.0中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍。

5.3、面试题

• ArrayList和 Linkedlist的区别
  二者都线程不安全,相比线程安全的 Vector,ArrayList执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,Linkedlist基于链表的数据结构。对于随机访问get和set,ArrayList绝对优于Linkedlist,因为Linkedlist要移动指针。对于新增和删除操作add(特指插入)和 remove,Linkedlist比较占优势,因为 ArrayList要移动数据。

• ArrayList和 Vector的区别
  Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比 ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack.

6、Collection子接口:Set接口

  • Set接口是Collection的子接口,set接口没有提供额外的方法;
  • Set集合不允许存储相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败。(多用于过滤操作,去掉重复数据)
  • Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法。
  • 用于存放无序的、不可重复的元素;无序性不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的;不可重复性:保证添加的元素照equals()判断时,不能返回true。即:相同的元素只能添加一个。
  • 向Set中添加的数据,其所在的类一定要重写hashCode()和equals();主要指:HashSet、LinkedHashSet。

6.1、添加元素过程

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

6.2、常用实现类

6.2.1、HashSet

• Hashset是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
• HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
• HashSet不能保证元素的排列顺序,线程是不安全的,集合元素可以是nul
• HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。

6.2.2、LinkedHashSet

• LinkedhashSet是HashSet的子类
• LinkedhashSet根据元素的hashCode值来决定元素的存储位置但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
• LinkedhashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
• LinkedhashSet不允许集合元素重复。

6.2.3、TreeSet

• Treeset是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
• TreeSet底层使用红黑树结构存储数据
• 向TreeSet中添加的数据,必须是相同类型的对象,不能即是int又是String,添加对象也是一样。
• 不能存储null值。

6.2.3.1、TreeSet排序方式:

• TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。
• 自然排序中,比较两个对象是否相同的标准为:compareTo() 返回0.不再是equals().
• 定制排序中,比较两个对象是否相同的标准为:compare() 返回0.不再是equals().

方式一:自然排序
  自然排序 TreeSet会调用集合元素的compareTo(object obj)方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列。如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口;实现Comparable接口的类必须实现compareTo(Object obj)方法,对象是通过该方法的返回值来比较大小;向TreeSet中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
在这里插入图片描述

方式二:定制排序
  通过 实现Comparator接口并且需要重写 compare(T o1,T o2)方法。 利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
  定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。

public static void main(String[] args) {
    Comparator comparator= new Comparator() {
        //照年龄从小到大排列
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof User && o2 instanceof User) {
                User u1 = (User) o1;
                User u2 = (User) o2;
                return Integer.compare(u1.getAge(), u2.getAge());
            } else {
                throw new RuntimeException("输入的数据类型不匹配");
            }
        }
    };

	//comparator实例作为形参传递给TreeSet的构造器
    TreeSet set = new TreeSet(comparator);
    set.add(new User("Tom", 12));
    set.add(new User("Jerry", 32));
    set.add(new User("Jim", 2));
    set.add(new User("Mike", 65));
    set.add(new User("Mary", 33));
    set.add(new User("Jack", 33));
    set.add(new User("Jack", 56));

    Iterator iterator = set.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

三、Map接口

在这里插入图片描述

• Map与Collection并列存在。用于保存具有映射关系的数据:key-value
• Map中的key和value都可以是任何引用类型的数据
• Map中的key用set来存放不允许重复,即同一个Map对象所对应的类,须重写 hashCode()和 equals()方法
• 常用 String类作为Map的“键”
• key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的、确定的value
• Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是Map接口使用频率最高的实现类

1、HashMap

• HashMap是Map接口使用频率最高的实现类。
• 允许使用null键和null值。
• HashMap判断两个key相等的标准是:两个key通过equals()方法判断,如果返回true,则hashCode值也相等。
• 线程不安全的,效率高;
• 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()
• 一个key-value构成一个entry,所有的entry构成的集合是Set:无序的、不可重复的。所以遍历HashMap需要先map.entrySet()才能遍历。

1.1、HashMap实现原理

  HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

1.2、HashMap数据结构

a、JDK 7.0及以前的版本:HashMap是数组+链表结构(地址链表法)
b、在JDK 8.0中,存储结构=数组+链表+红黑树的组合;
在这里插入图片描述

1.3、jdk1.7 及以前

a、添加元素的过程
HashMap map = new HashMap()
//在实例化以后,底层创建了长度是16的一维数组Entry[] table。
map.put(key1,value1)
  1. 首先,调用key1所在类的hashCode()计算key1存储位置哈希值,此哈希值经过某种算法计算以后,得到在Entry[]数组中的存放位置。
  2. 如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
  3. 如果此位置上的数据不为空,意味着此位置上存在一个或多个数据(以链表形式存在);那么把key1的值和已经存在的一个或多个数据的值用equals()方法进行比较。
  4. 如果key1的值与已经存在的数据的值都不相同,此时key1-value1以链表形式添加成功。----情况2
  5. 如果key1的值和已经存在的某一个数据(key2-value2)的值相同,那么用key1需要新添加的值替换原来key2的值。 ----情况3
b、HashMap的扩容

  当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对 HashMap的数组进行扩容,而在HashMap数组扩容之后,原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是 resize。

c、HashMap扩容机制

  当HashMap中的元素个数超过阈值(threshold) = 负载因子(loadFactor) x 数组容量(capacity),就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_ FACTOR)为0.75,这是一个折中的取值。也就是说,默认情况下,数组容量(DEFAULT INITIAL CAPACITY)为16,那么当 HashMap中元素个数超过16 x 0.75=12(这个值就是代码中的 threshold值,也叫做阈值)的时候,就把数组的大小扩展为2 * 16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知 HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

1.4、HashMap在JDK 8.0及以后

a、 HashMap在JDK 8.0中,存储结构=数组+链表+红黑树的组合;

JDK 8.0底层的数组是:Node[];而非Entry[]
在这里插入图片描述

b、添加元素的过程

  当实例化一个HashMap时,会初始化 initialCapacity(初始容量16)和loadFactor(加载因子),在put第一对映射关系时,系统会创建一个长度为 initialCapacity的Node数组,这个长度在哈希表中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为“桶”( bucket),每个桶都有自己的索引,系统可以根据索引快速的查找桶中的元素。每个 桶中存储一个元素,即一个Node对象,但每一个Noe对象可以带一个引用变量next,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Node链表。也可能是一个一个 TreeNode对象, 当Node链表的对象个数如果达到了8个,并且桶的长度大于64,那么这个链表会变成红黑树,当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

2、LinkedHashMap

• LinkedHashMap底层使用的结构与HashMap相同,因为LinkedHashMap继承于HashMap
• 在遍历map元素时,可以照添加的顺序实现遍历。原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
• 对于频繁的遍历操作,此类执行效率低于HashMap。

3、TreeMap

• TreeMap存储Key-Value对时,需要根据key-value对进行排序。TreeMap可以保证所有的 Key-Value对处于有序状态。
• TreeSet底层使用红黑树结构存储数据
• TreeMap的Key的排序:

  1. 自然排序: TreeMap的所有的Key必须实现Comparable接口,而且所有的Key应该是同一个类的对象,否则将会抛出ClasssCastEXception()
  2. 定制排序:创建 TreeMap时,传入一个 Comparator对象,该对象负责对TreeMap中的所有key进行排序。此时不需要Map的Key实现Comparable接口

• TreeMap判断两个key相等的标准:两个key通过 compareTo()方法或者compare()方法返回0.
• TreeMap的key不能存null值。

4、Hashtable

• Hashtable是个古老的Map实现类,JDK1.0就提供了。不同于 HashMap,Hashtable是线程安全的.
• Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用
• 与HashMap.不同,HashTable不允许使用null作为key和value.

5、Properties

• Properties类是Hashtable的子类,该对象用于处理属性文件
• 由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型
• 存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

public static void main(String[] args) {
    FileInputStream fis = null;
    try {
        Properties pros = new Properties();

        fis = new FileInputStream("E://jdbc.properties");
        pros.load(fis);//加载流对应的文件

        String name = pros.getProperty("name");
        String password = pros.getProperty("password");
        System.out.println("name = " + name + ", password = " + password);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

6、 常用方法

添加、删除、修改操作
•	Object put(Object key,Object value);	//将指定key-value添加到(或修改)当前map对象中void putAll(Map m);						//将m中的所有key-value对存放到当前map中Object remove(Object key);				//移除指定key的key-value对,并返回valuevoid clear();							//清空当前map中的所有数据Object put(Object key,Object value);	//将指定key-value添加到(或修改)当前map对象中void putAll(Map m);						//将m中的所有key-value对存放到当前map中Object remove(Object key);				//移除指定key的key-value对,并返回valuevoid clear();							//清空当前map中的所有数据

元素查询的操作
•	Object get(Object key);					//获取指定key对应的valueboolean containsKey(Object key);		//是否包含指定的keyboolean containsValue(Object value);	//是否包含指定的valueint 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集合

四、Collections工具类的使用

1、作用

Collections是一个操作Set、Lit和Map等集合的工具类;Collections中提供了一系列静态的方法对集合元素进行排序、査询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

2.常用方法

排序操作
•	reverse(List); 				//反转 List 中元素的顺序shuffle(List);				//对 List 集合元素进行随机排序sort(List);					//根据元素的自然顺序对指定 List 集合元素升序排序sort(ListComparator);		//根据指定的 Comparator 产生的顺序对 List 集合元素进行排序swap(Listint i, int j);	//将指定 list 集合中的 i 处元素和 j 处元素进行交换

查找、替换
•	Object max(Collection);//根据元素的自然顺序,返回给定集合中的最大元素Object max(CollectionComparator);//根据 Comparator 指定的顺序,返回给定集合中的最大元素Object min(Collection);//根据元素的自然顺序,返回给定集合中的最小元素Object min(CollectionComparator);//根据 Comparator 指定的顺序,返回给定集合中的最小元素int frequency(CollectionObject);//返回指定集合中指定元素的出现次数void copy(List dest,List src);//将src中的内容复制到dest中boolean replaceAll(List list,Object oldVal,Object newVal);//使用新值替换 

3、同步控制

Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
在这里插入图片描述
示例

@Test
public void test3() {
    List list = new ArrayList();
    list.add(123);
    list.add(123);
    list.add(123);
    list.add(43);
    list.add(765);
    list.add(-97);
    list.add(0);
    System.out.println(list);//[123, 43, 765, -97, 0]
    //返回的list1即为线程安全的List
    List list1 = Collections.synchronizedList(list);
    System.out.println(list1);//[123, 123, 123, 43, 765, -97, 0]
}

面试题:
所以集合都能存储null值吗?
1、Vector、Arraylist、LinkedList可以存储多个null.

2、HashSet、Linkedset可以存储一个null. TreeSet不能存储null.

3、HashMap 、LinkedHashMap key与value均可以为null. treemap key不可以为null,value可以为null.

4、HashTable、ConcurrentHashMap key与value均不能为null.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值