集合(Collection)

来源:互联网 发布:文档识别软件 编辑:程序博客网 时间:2024/05/16 11:44

集合

集合基本结构:

  • Iterable
  • Collection
    • Set
      • HashSet
      • TreeSet
    • List
      • ArrayList
      • LinkedList
    • Map
      • HashMap
      • TreeMap
      • Properties(配置文件要用)
      • Collections(集合工具类)

集合类&容器:

为什么出现集合类?

面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就要对对象进行存储,集合就是存储对象最常用的一种方式。

数组和集合类同是容器,有何不同?

数组虽然也可以存储对象,但其长度是固定的;集合长度是可变的。数组中可以存储任意数据类型,集合只能存储对象。

集合类的特点

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

注意

数组只能装同一种数据类型的数据;数组的长度不能动态改变;

集合框架的构成及分类:

这里写图片描述

为啥出现这么多的容器:每个容器对数据的存储方式不同:

这里写图片描述

两大接口:

Java集合类主要由两个接口派生出来:

  • Collection
    • Set :无序不可重复。
      • SortedSet :可对集合元素排序
    • List :有序可重复。
    • Queue :队列
    • Map
      • SortedMap :可对集合元素排序

Collection接口里面的常用方法:

  1. boolean add(Object o):该方法用于向集合里面添加一个元素,若集合对象被添加操作改变了,返回true.
  2. boolean addAll(Collection c):把集合c里面的所有元素添加到指定集合里面去,如果集合对象被添加操作改变了返回true.
  3. void clear():清除集合里面的所有元素,将集合长度变为0。
  4. boolean contains(Object o):返回集合里是否包含指定的元素。
  5. boolean containsAll(Collection c):返回集合里是否包含集合c内所有的元素。
  6. boolean isEmpty():返回集合是否为空(长度是否为0)。
  7. Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。
  8. boolean remove(Object o):删除集合中指定元素o。
  9. boolean removeAll(Collection c):从集合中删除集合c里面的元素。若删除一个或以上返回true。
  10. boolean retainAll(Collection c):从集合中删除集合c里不包含的元素。
  11. int size():得到集合元素的个数。
  12. Object[] toArray():把集合转成一个数组,所有集合元素变成数组元素。

注意:

集合中存储的都是对象的地址;可使用StringBuilder证明是存的地址

集合遍历输出方式:

  1. Iterator:迭代输出,一旦操作集合的遍历输出,首选Iterator接口;
  2. ListIterator:Iterator子接口,专门输出List中的元素;
    3.Enumeration:古老的输出方式,迭代Vector元素,现已被Iterator取代;
  3. foreach:可输出数组和Iterable对象;

JDK源代码:

public interface Iterable<T> {    /**     * Returns an iterator over a set of elements of type T.     *实现这个接口的类,允许其对象成为 "foreach" 语句的目标。     * @return an Iterator.     */    Iterator<T> iterator();}

Iterator接口里的常用方法:

Iterator主要遍历Collection集合中的元素,也有称为迭代器或迭代精灵。
1. boolean hasNext():若被迭代的集合元素还没有被遍历,返回true.
2. Object next():返回集合的下一个元素.
3. void remove():删除集合上一次next()方法返回的元素。(若集合中有多个相同的元素,都可以删掉).
iterator对于集合才能用,for不同,只要是循环都可用。
迭代是取出集合中元素的一种方式。
因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
迭代器的next方法返回值类型是Object,所以要记得类型转换。

Iterator接口的使用代码:

Set<Integer> l = new Set<Integer>();..................Iterator iter = l.iterator();while(iter.hasNext()){    System.out.println(iter.next());}

Set接口:

Set是Collection子接口;
Set和Collection基本上一样,一点除外:
Set无法记住添加的顺序,不允许包含重复的元素。
当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。
Set判断两个对象是否相等用equals,而不是使用==。
也就是说当两个对象equals比较返回true时,Set集合是不会同时接受这个两个对象的。
常用子类:
HashSet:底层用Hash算法求得元素的存储位置。
TreeSet:底层用红黑树算法求得元素的存放位置。

hashCode方法对于HashSet的作用:

HashSet类是Set接口最常用的实现类,采用hash算法求得元素存储存储的位置,具有良好的存储和查找功能。
散列存储:不记录添加顺序;排列顺序时,顺序有可能发生变化;
线程不安全的,多个线程访问一个HashSet要使用同步代码;(思考:当多个线程同时访问一个HashSet时会出现什么情况?)
HashSet集合元素值允许是null,但是最多只能有一个;

hash(翻译为哈希,或散列)算法的功能:

保证通过一个对象快速找到另一个对象;
其算法价值体现在速度,可以保证查询快速执行;
当从HashSet中访问元素时,HashSet先计算该元素的hashCode(也就是该对象的hashCode方法返回值),然后直接到该HashCode对应的位置取出该元素;
在这里对象的hashCode就好比是数组里的索引,但是又不是索引;

HashSet元素添加:

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致:若不一致:直接添加进去;若一致,再进行equals方法比较,equals方法如果返回true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加进去;
如果我们重写了equals方法,也要重写hashCode方法,反之亦然;。
HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。
如果需要某个类的对象保存到HashSet集合中,覆写该类的equals()和hashCode()方法,应该尽量保证两个对象通过equals比较返回true时,他们的hashCode返回也相等。

Set 判断两个对象是否相等,不是使用 == 运算符判断,而是equals方法. 怎么证明?

public static void main(String[] args){    Set names = new HashSet();    names.add(new String("Will"));    boolean b = namse.add(new String("Will"));    System.out.println("是否添加成功= "+b);    System.out.println(names);}

不同类型的字段如何求得其hashCode:

字段类型(f) 计算方式 boolean hashCode=(f?0:1); byte,short,char,int hashCode=(int)f; long hashCode=(int)(f^(f>>>32)); float hashCode=Float.floatToIntBits(f); double long l=Double.doubleToLongBits(f) hashCode=(int)(l^(l>>>32)) 引用类型 hashCode=f.hashCode();

一般,为了避免两个不同字段的hashCode和相等,我们每个字段获取hashCode后再乘以一个质数; 如: (int)f1 * 7 + f2.hashCode() * 23;(f1,f2表示不同的字段)

TreeSet:

使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序;
参与排序的元素必须是同一类型的,不然会发生ClassCastException异常;
TreeSet是SortedSet接口唯一的实现,与HashSet相比额外的方法有:
Comparator comparator():返回当前Set使用的Comparator,若返回null,表示以自然顺序排序。
Object first() 返回此 set 中当前第一个(最低)元素。
Object last() 返回此 set 中当前最后一个(最高)元素。
SortedSet subSet(Object fromElement, E toElement) 返回此 set 的部子集,其元素从 fromElement(包括)到 toElement(不包括)。
SortedSet headSet(Object toElement)返回此 set 的部分子集,其元素严格小于 toElement。
SortedSet tailSet(Object fromElement) 返回此 set 的部分子集,其元素大于等于 fromElement。

注意:

TreeSet并不是根据元素的插入顺序排序的,而是根据实际的值进行排序的;

TreeSet的排序之自然排序:

  1. TreeSet会调用元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合里的元素按升序排列.此时需要排序元素的类必须实现Compareble接口,并覆写其int compareTo(Object o)方法;
  2. 该方法用于比较对象,若:obj1.compareTo(obj2),返回0,表示两个对象相等,若返回一个正整数,表示obj1大于obj2,若返回一个负整数,表示obj1小于obj2;
    对于TreeSet集合而言,判断两个对象相等的标准是:compareTo()方法比较返回 0;

注意:

TreeSet底层采用的是红黑树的数据结构对元素进行排序的:红黑树是一种自平衡二叉查找树。

TreeSet的排序之定制排序:

  1. TreeSet的自然排序是根据元素的大小进行升序排序的,若想自己定制排序,比如降序排序,就可以使用Comparator接口了:
  2. 该接口包含int compare(Object o1,Object o2)方法,用于比较两个对象的大小,比较结果和compareTo方法一致;
  3. 要实现定制排序,需要在创建TreeSet集合对象时,提供一个一个Comparator对象,该对象里负责集合元素的排序逻辑;
    TreeSet(Comparator comparator)

List接口:

Collection的子接口;
List是有序的集合,集合中每个元素都有对应的顺序序列。List集合可使用重复元素,可以通过索引来访问指定位置的集合元素(顺序索引从0开始),List集合默认按元素的添加顺序设置元素的索引,比如第一个元素的索引就是0,好似数组。
List作为Collection子接口当然拥有其所有方法,同时也有自己的方法:
1. void add(int index,Object e):将元素e添加到List集合中的index处;
2. boolean addAll(int index,Collection c):将集合c所包含的所有元素都插入在List集合的index处;
3. Object get(int index):返回集合index索引处的元素;
 int indexOf(Object o):返回对象o在List集合中第一次出现位置的索引;
4. int lastIndexOf(object o):返回对象o在List集合中最后一次出现的位置索引;
5. Object remove(int index):删除并返回index索引处的元素;
6. Object set(int index,Object e):把集合index处的元素替换为e对象,返回以前在指定位置的元素;
7. List subList(int fromIndex,int toIndex):返回从所有fromIndex(包括)到toIndex(不包括)处所有集合元素的子集合。

ListIterator接口:

是Iterator的子接口,专门用于操作List集合的输出;
1. List自己还有一个listIterator()方法,该方法返回ListIterator对象.

在Iterator上额外增加的方法:
1. 支持双向输出:
2. boolean hasPrevious():返回该迭代器关联集合是否还有上一个元素;
3. Object previous():返回该迭代器的上一个元素;

List接口中常用类:

  1. Vector:线程安全,但速度慢,已被ArrayList替代。
  2. ArrayList:线程不安全,底层由数组结构实现,查询速度快。
  3. LinkedList:线程不安全,底层由链表结构实现,增删速度快。

取出List集合中元素的方式:
1. get(int index):通过脚标获取元素。
2. iterator():通过迭代方法获取迭代器对象。

ArrayList和Vector类都是基于数组实现的List类,Vector比较古老,被ArrayList取代了;

ArrayList是线程不安全的,而Vector是线程安全的,但是即使这样,也不推荐使用Vector,因为Collections工具类有方法可以得到线程安全的ArrayList对象;

Collections类: static List synchronizedList(List list) 返回指定列表支持的同步(线程安全的)列表。

Queue接口:

继承Collection接口
模拟队列:先进先出(FIFO);
队列都是在队尾入队,在队头出队。
1. void add(Object e):将e插入到队列尾部;
2. Object element():获取队列头部的元素;
3. boolean offer(Object e):将e插入到队列的尾部,当使用有容量限制的队列时,此方法比add(Object e)方法更好。
4. Object peek():获取队列头部的元素。如果此双端队列为空,则返回 null。
5. Object poll():获取并删除队列头部的元素。如果此双端队列为空,则返回 null。
6. Object remove():获取并删除队列头部的元素。

LinkedList接口:

LinkedList实现了Deque接口,而Deque是Queue的子接口,因此LinkedList可以作为(双端)队列使用。而LinkedList底层又是由双端链表实现的,因此LinkedList又可以当做栈来使用。

Deque接口中自定义方法:
1. void addFirst(Object e):把元素插入到该双向队列的开头(在队头添加元素);
2. void addLast(Object e):把该元素插入到该双向队列的末尾(在队尾添加元素)。
3. Object getFirst():获取但不删除队列第一个元素(队头元素);
4. Object getLast():获取但不删除队列最后一个元素(队尾元素);
5. boolean offerFirst(Object e):将指定的元素插入到该双向队列的开头(队头);
6. boolean offerLast(Object e):将指定元素插入到双向队列的末尾(队尾);
7. Object removeFirst():删除第一个元素(队头元素);
8. Object removeLast():删除最后一个元素(队尾元素);
9. Object peekFirst():获取但不删除队列第一个元素(队头元素),如队列为null,返回null;
10. Object peekLast():获取但不删除队列最后一个元素(队尾元素),如队列为null,返回null;
11. Object pollFirst():获取并删除队列第一个元素(队头元素),如队列为null,返回null;
12. Object pollLast():获取并删除队列最后一个元素(队尾元素),如队列为null,返回null;
13. Object pop():从此双端队列所表示的堆栈中弹出一个元素(栈顶元素)。
14. void push(Object e):将e推入进该队列栈中(当作新的栈顶)。
15. Object removeFirst():获取并删除队列第一个元素(队头元素)。
16. Object removeFirstOccurrence(Object o):删除队列第一次出现的o元素;
17. removeLast():获取并删除队列最后一个元素(队尾元素);
18. removeLastOccurrence(Object o):删除队列中最后一次出现的o元素;

Map接口:

表示映射关系,也有人称为字典,Map集合里存在两组值,一组是key,一组是value。Map里的key不允许重复。通过key总能找到唯一的value与之对应。(Map里面的Key需要满足Set集合的要求,其实Map集合就是封装Set集合而来的!!!)

Map里的key集存储方式和对应的Set集合中的元素存储方式一致;
学生都有一个学号,我们点名是点学号就能找到某个学生,好比这个学号就是key,学生就是value。

Map.Entry是Map接口的内部接口,专门用来保存key-value内容.

Map集合的数学解释:假设x 与y 具有映射关系: x 取一值,y有且只有一个值对应,而y 取一值,x 可以有多个值对应。

Map接口中常用的方法:

  1. void clear():删除该Map对象中所有的key-value对。也就是清理该集合;
  2. boolean containsKey(Object key):查询Map中是否包含指定的key;
  3. boolean containsValue(Object value):查询Map中是否包含至少一个特定的value;
  4. Set entrySet():返回Map所包含的key-value对所组成的Set集合,每个集合元素都是Map.Entry对象(Entry是Map内部类);
  5. Object get(Object key):返回指定key所对应的value,若此Map中不包含该key,返回null;
  6. boolean isEmpty():判断Map集合是否为空;
  7. Set keySet():返回该Map中所有key所组成的Set集合;
  8. Object put(Object key,Object value):添加一个key-value对,若Map中已有与key相等的key-value对,则新的key-value对覆盖原来的key-value对;
  9. void putAll(Map m):将m中的key-value赋值到调用该方法的Map对象中;
  10. Object remove(Object key):删除指定key所对应的key-value对,返回本删除key所关联的value,若key不存在,返回null;
  11. int size():返回该Map里面key-value对的个数;
  12. Collection values():返回Map里所有value组成的Collection。

Map.Entry接口:

Entry接口是Map接口里面的一个内部接口.

该接口用于封装key- value,有3个方法:
1. Object getKey();返回Entry里包含的key值
2. Object getValue();返回Entry里包含的value值
3. Object setValue(Object value):设置Entry里包含的value值,并返回新设置的value值;

使用Map.Entry接口的实例遍历HashMap的代码:

import java.util.HashMap;import java.util.Map.Entry;import java.util.Set;/***输出结果:*3--王刚*2--李国立*1--张国立*4--王某某*/public class Main2 {    public static void main(String[] args){        HashMap<String, String> p = new HashMap<>();        p.put("1", "张国立");        p.put("2", "李国立");        p.put("3", "王刚");        p.put("4", "王某某");        Set<Entry<String, String>> s = p.entrySet();        for (Entry<String, String> entry : s) {            String number = entry.getKey();            String name = entry.getValue();            System.out.println(number+"--"+name);        }    }}

Map集合的输出:

按照最正统的做法,所有的Map集合的内容都要依靠Iterator实例输出。

以上虽然是完成了输出,但是完成的不标准,Map集合本身并不能直得到Iterator接口的实例,如果此时非要使用Iterator实例输出Map集合中的内容的话,则要采用如下的步骤:
方法一:
1. 通过entrySet方法变成Set对象
2. 调用Set的Iterator方法,此时每个Iterator对象集合,存放的就是是Map.Entry对象的引用。
3. 使用Iterator实例得到Map.Entry对象,再从Map.Entry对象中分离出 key - value

方法一代码:

import java.util.HashMap;import java.util.Iterator;import java.util.Map.Entry;import java.util.Set;/** * 运行结果: * 3--王刚 * 2--李国立 * 1--张国立 * 4--王某某 * */public class Main2 {    public static void main(String[] args){        HashMap<String, String> p = new HashMap<>();        p.put("1", "张国立");        p.put("2", "李国立");        p.put("3", "王刚");        p.put("4", "王某某");        Set<Entry<String, String>> s = p.entrySet();        Iterator<Entry<String, String>> i = s.iterator();        while (i.hasNext()) {            Entry<String, String> en = i.next();            System.out.println(en.getKey()+"--"+en.getValue());        }    }}

方法二:
1. 通过keySet得到Map集合所以key的Set集合
2. 调用Set的Iterator方法,此时每个Iterator对象是key值
3. 通过Map的getValue(key)得到value值

Map集合中的常用类:

Hashtable:线程安全,速度慢,不允许存放null键,null值,已被HashMap替代,无序存放。Hashtable是很老的Map实现类,比Map接口更老,很少使用;

HashMap:线程不安全,速度快,允许存放null键,null值。类中的key都属于无序存放的。 即使HashMap线程不安全,在多线程中也推荐使用。
使用Collections类: public static Map synchronizedMap(Map m)方法来返回线程同步的映射;

HashMap里的key的存储和保存与HashSet里面的元素一致,若自定义类作为HashMap或Hashtable的key:当两个key对象的equals方法返回true时,两个key对象的hashCode值也应一样;

TreeMap:对key进行排序,排序原理与TreeSet相同。

TreeMap类:

HashMap子类中的key都属于无序存放的,如果现在希望有序(按key排序)则可以使用TreeMap类完成,但是需要注意的是,由于此类需要按照key进行排序,而且key本身也是对象,那么key对象所在的类就必须实现Comparable接口。

当 使用 TreeMap 的时候 key不能为null,value可以是 null;

TreeMap对key的排序方式:
自然排序;
定制排序;
TreeMap判断两个key元素是否相等标准(和HashSet一样):compareTo方法或compare方法返回0;

Properties类:

是Hashtable的子类;
Properties类的主要功能是用于操作属性,在各个语言(包括操作系统)都会存在着许多的配置文件。所有的属性文件中的属性都是按照“key=value”的形式保存的,而且保存的内容都是String(字符串)。

Properties类的常用方法:

  1. public Object setProperty(String key,String value):设置属性;
  2. public String getProperty(String key):根据属性的名字取得属性的内容,如果没有返回null结果;
  3. public String getProperty(String key,String defaultValue):根据属性的名字取得属性内容,如果没有则返回默认(defaultValue);
  4. void load(InputStream/Reader inStream)
  5. void loadFromXML(InputStream in)
  6. public void list(PrintStream out):从一个输出流中显示所有的属性内容;
  7. public void store(OutputStream/Writer out, String comments)
  8. public void storeToXML(OutputStream os, String comment)

Proterties类使用示例:

文件结构:
这里写图片描述
配置文件内容:
这里写图片描述

properties工具类代码:

import java.io.IOException;import java.io.InputStream;import java.util.Properties;/** * 专门操作properties文件的工具类 * @author lineng * */public class PropertiesUtil {    /**     * 获取指定key(键值)的value(值)     * @param key     * @return     */    public static String getValue(String key){        Properties pro = new Properties();        InputStream in = new PropertiesUtil().getClass().        getResourceAsStream("/notes.properties");        try {            pro.load(in);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return (String)pro.get(key);    }}

properties类使用代码实例2:

import java.io.IOException;import java.io.InputStream;import java.util.Properties;public class PropertiesDemo {    public static void main(String[] args) {        /**         * java.lang.Object                java.util.Dictionary<K,V>                    java.util.Hashtable<Object,Object>                        java.util.Properties         */        //Properties对象要求key和value都是String        Properties p  = new Properties();        /**         * Properties对象是可以调用Map里的所有方法的         */        //Object setProperty(String key, String value)          p.setProperty("name1", "春哥哥");        p.setProperty("name2", "凤姐");        System.out.println(p);        /**         * String getProperty(String key)                   用指定的键在此属性列表中搜索属性。            String getProperty(String key, String defaultValue)                   用指定的键在属性列表中搜索属性。          */        System.out.println(p.getProperty("name2"));        System.out.println(p.getProperty("name2","嘻嘻嘻嘻"));        System.out.println(p.getProperty("name3"));        System.out.println(p.getProperty("name3","哈哈哈哈哈"));        /*         * 加载资源         * */        InputStream in  = Thread.currentThread()        .getContextClassLoader()        .getResourceAsStream("/notes.properties");        try {            p.load(in);        } catch (IOException e) {            e.printStackTrace();        }        System.out.println(p);    }}

>

Collections类:

操作集合的工具类,常用方法如下:
1. static void reverse(List list):反转指定List集合中的顺序;
2. static void shuffle(List list):对集合元素随机排序
3. static void sort(List list):自然升序排序
4. static vois swap(List list,int i, int j):将指定的List集合i处元素和j处元素进行交换;
5. static void rotate(List list, int distance):
若distance为正数,将list集合后的distance个元素移到前面;
当distance为负数,将list集合前的distance个元素移到后面;
6. static int binarySearch(List list, Object key) 使用二分搜索法搜索指定列表,以获得指定对象。调用之前 必须调Collections.sort(List list)(完成自然排序);
7. static Object max(Collection coll) 根据元素的自然顺序,返回给定 collection 的最大元素。
8. static Object min(Collection coll) 根据元素的自然顺序,返回给定 collection 的最小元素。
9. static void fill(List list, Object obj) 使用指定元素替换指定列表中的所有元素。
10. static int frequency(Collection c, Object o) 返回指定 collection 中等于指定对象的元素数。
11. static int indexOfSubList(Listsource, List target) 返回指定源列表中第一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。
12. static int lastIndexOfSubList(List source, List target) 返回指定源列表中最后一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。
13. static boolean replaceAll(List list, Object oldVal, Object newVal) 使用另一个值替换列表中出现的所有某一指定值。
14. 同步集合

Arrays类:

  1. public static List asList(Object… a)返回一个受指定数组支持的固定大小的列表(返回的是不可变的List(长度固定))。
    (对返回列表的更改会“直接写”到数组。)此方法同 Collection.toArray() 一起,充当了基于数组的 API 与基于 collection 的 API 之间的桥梁。返回的列表是可序列化的,并且实现了 RandomAccess。
    此方法还提供了一个创建固定长度的列表的便捷方法,该列表被初始化为包含多个元素:
    List list= Arrays.asList(“Larry”, “Moe”, “Curly”);
原创粉丝点击