第一部分

集合是Java存放对象的容器,存放在java.util包下,下图是Java集合类的继承与实现关系:

Collection

解释:

  • Collection接口是集合类的接口,List、Set、Queue的父接口。

    • Set中不能包含重复的元素;
    • List是一个有序的集合可以包含重复的元素,提供了按索引访问的方式。
  • Map是java.util包中的另一个接口,和Collection接口没有关系,相互独立。都属于集合类。

    • Map包含了key-value对。
    • Map不能包含重复的key,可以包含相同的value。
  • Iterator(迭代器),所有的集合类都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含三种方法:

    • 1、hasNext():是否还有下一个元素;
    • 2、next():返回下一个元素;
    • 3、remove():删除当前元素

重要的类和接口介绍:

  • 1、List(有序、可重复)

    • List关注的是索引,拥有一系列和索引相关的方法,查询速度快。

    • 在向list集合中插入或删除数据时,会有后面数据的移动,所以插入删除数据速度慢。

    • 1.1 ArrayList和LinkedList

      • 在用法上区别不大,功能上有差别。LinkedList经常用在增删操作较多而查询操作很少的情况,ArrayList相反。
  • 2、Set(无序、不能重复)

    • Set理存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只能简单地把对象加入集合中。
  • 3、Map(键值对、键唯一、值不唯一)

    • Map集合中存储的是键值对,键不能重复,值可以重复。

    • 根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。

    • 3.1 Map的实现类:HashMap、HashTable、LinkedHashMap和TreeMap

    • 3.2 HashMap

      • 根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度;
      • 遍历时,取得数据的顺序完全是随机的;
      • 因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为null,是非同步的。
    • 3.3 HashTable

      • 和HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,他继承自Dictionary(字典)类,不同的是它不允许记录的键或者值为null,同时效率较低。
    • 3.4 ConcurrentHashMap

      • 线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hashtable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
    • 3.5 LinkedHashMap

      • LinkedJHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历时会比hashMap慢,有HashMap的全部特性。
    • 3.6 TreeMap

      • TreeMap实现了SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排列(自然顺序)也就是指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步。

集合表结构

  • 4、集合类的Iterator遍历:
Iterator it = arr.iterator();
while(it.hasNext()){
    object o = it.next();
    ...
}
  • 5、Map的遍历
    • 第一种:KeySet()
      • 将Map中所有的键存入到set集合中,因为set具备Iterator,所以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。keySet():迭代后只能通过get()取key。
      • 取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。
典型用法:
Map map = new HashMap();
map.put("key1","lisi1");
map.put("key2","lisi2");
map.put("key3","lisi3");
map.put("key4","lisi4");

//先获取map集合的所有键的set集合,keyset()
Iterator it = map.keySet().iterator(); //获取迭代器

while(it.hasNext()){
    Object key = it.next();

    System.out.println(map.get(key));
}
  • 第二种:entrySet()
    • Set> entrySet() //返回此映射中包含的映射关系的 Set 视图。(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的。
    • Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()两种方法来取key和value。返回的是Entry接口。
//典型用法
Map map = new HashMap();

map.put("key1","lisi1");
map.put("key2","lisi2");
map.put("key3","lisi3");
map.put("key4","lisi4");

//先获取map集合的所有键的set集合,keyset()
Iterator it = map.keySet().iterator(); //获取迭代器

while(it.hasNext()){
    Entry e = (Entry)it.next();

    System.out.println("键" + e.getKey() + "的值是:" + e.getValue());
}

entrySet()方法的效率更高一些!

对于keySet其实是遍历了2次,一次是转为iterator,一次就是从HashMap中取出key所对于的value。而entryset只是遍历了第一次,它把key和value都放到了entry中,所以快了。两种遍历的遍历时间相差还是很明显的。

主要实现类区别总结:

  • vector和ArrayList

    • 1、vector是线程同步的,也是线程安全的;arrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arrayList效率比较高。

    • 2、如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。

    • 3、如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linkedlist,因为它移动一个指定位置的数据时其它元素不移动。

    • 4、ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。

  • arrayList和linkedList

    • 1、arraylist是实现了基于动态数组的数据结构,LinkedList实现了基于链表的数据结构。

    • 2、对于随机访问get和set,ArrayList觉得优先于Linked List,因为LinkedList要移动指针。

    • 3、对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

  • HashMap和TreeMap

    • 在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。

    • 两个map中的元素一样,但顺序不一样,导致hashCode()不一样。

    • 同样做测试:

      • 在HashMap中,同样的值的map,顺序不同,equals时,false;
      • 而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。
  • HashTable和HashMap

    • 1、同步性:Hashtable是线程安全的,也就是同步,而HashMap是线程不安全的,不是同步的。
    • 2、HashMap允许存在一个为null的key,多个为null的value 。
    • 3、hashtable的key和value都不允许为null。

Java集合类图

图一

整理出如下信息: 图二

集合详解

HashSet

是Set接口的一个子类;
主要特点:

  • 里面不能存放重复元素,元素的插入和输出顺序不一致
  • 采用散列的存储方法,所以没有顺序
  • 代码实例:
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class HashSetTest {

    public static void main(String[] args) {

        Set set = new HashSet();

        set.add("a");
        set.add("b");
        set.add("c");
        set.add("d");
        set.add("f");
        //插入重复元素,测试set是否可以存放重复元素
        set.add("a");
        set.add(null);
        //插入重复null,看结果是否可以存放两个null
        set.add(null);

        Iterator iter = set.iterator();

        System.out.println("输出的排列顺序为:");

        while (iter.hasNext()){

            System.out.println( iter.next());
        }

    }
}

HashSet存放的值无序切不能重复,可以存放null,但只能存放一个null值.

HashSet继承AbstractSet,有两个重要的方法,其中HashCode()和equals()方法,当对象被存储到HashSet当中时,会调用HashCode()方法,获取对象的存储位置。

HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等。

LinkedHashSet

  • LinkedHashSet是HashSet的一个子类
  • 只是HashSet底层用的HashMap,而LinkedHashSet底层用的LinkedHashMap
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetTest{
    public static void main(String[] args){
        Set set = new LinkedHashSet();

        set.add("a");
        set.add("b");
        set.add("c");
        set.add("d");
        set.add("e");

        System.out.println("LinkedHashSet存储值的排序为: ");

        for(Iterator it = set.iterator();it.hasNext();){
            System.out.println(it.next());
        }        
    }
}
// 此时,LinkedHashSet中的元素时候有序的

SortSet(接口)

学习源泉open in new window

  • sortSet是一个接口,里面(只有TreeSet这个实现可用)的元素一定是有序的。
  • 保证迭代器按照元素递增顺序遍历的集合。可以按照元素的自然顺序(参见 Comparable)进行排序, 或者按照创建有序集合时提供的 Comparator进行排序。
//源代码:
public interface SortedSet<E> extends Set<E> {

        
    //返回与此有序集合关联的比较器,如果使用元素的自然顺序,则返回 null。

    Comparator<? super E> comparator();

        
    //返回此有序集合的部分元素,元素范围从 fromElement(包括)到 toElement(不包括)。

    SortedSet<E> subSet(E fromElement, E toElement);

        
    //用一个SortedSet, 返回此有序集合中小于end的所有元素。

    SortedSet<E> headSet(E toElement);

    
    //返回此有序集合的部分元素,其元素大于或等于 fromElement。

    SortedSet<E> tailSet(E fromElement);
        
    //返回此有序集合中当前第一个(最小的)元素。

    E first();
        

    //返回此有序集合中最后一个(最大的)元素

    E last();

}

TreeSet

  • TreeSet类实现Set接口,该接口由TreeMap实例支持,此类保证排序后的set按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序 进行排序(参见 Comparable或按照在创建 set 时所提供的比较器进行排序)。
  • Set接口根据equals操作进行定义,但TreeSet实例将使用compareTo(或compare)方法执行所有的键比较。
import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetTest{

    static class Person{
        int id;
        String name;
        int age;

        public Person(int id, String name, int age){
            this.id = id;
            this.name = name;
            this.age = age;
        }

        public String toString(){
            return "id:" + this.id + " " + "name:" + this.name + " " + "age:" + this.age;
        }

    }

    static class MyComparator implements Comparator<Person>{
        @Override
        public int compare(Person p1, Person p2){
            if(p1 == p2){
                return 0;
            }
            if(p1 !=null && p2 == null){
                return 1;
            }
            else if(p1 == null && p2 !=null){
                return -1;
            }
            if(p1.id > p2.id){
                return 1;

            }else if(p1.id < p2.id){
                return -1;

            }
            
            return 0;
        }
    }

    public static void main(String[] args){
        MyComparator myComparator = new MyComparator();

        TreeSet<Person> treeSet = new TreeSet<>(myComparator);

        treeSet.add(new Person(3,"张三",20));
        treeSet.add(new Person(2,"王二",22));
        treeSet.add(new Person(1,"赵一",18));
        treeSet.add(new Person(4,"李四",29));

        //增加null空对象
        treeSet.add(null);

        System.out.println("TreeSet的排序是:");

        for (Person p : treeSet){
            if(p == null){
                System.out.println(p);
            }else {
                System.out.println(p.toString());
            }
        }

    }
}

实例用TreeSet保存对象引用,并且实现Comparator中compare方法进行比较和排序。
输出: 图三

表明TreeSet是可以按照自定义方法中的比较进行排序的,且可以有空值。

Vector

  • Vector 类也是基于数组实现的队列,代码与ArrayList非常相似。
  • 线程安全,执行效率低。
  • 动态数组的增长系数
  • 由于效率低,并且线程安全也是相对的,因此不推荐使用vector

Stack

  • 继承自vector类,栈
  • 主要的方法:
    • empty , boolean , 判空
    • peek , E , 返回栈顶元素
    • pop , E , 出栈
    • push , E , 入栈
    • search, E , 返回最靠近顶端的目标元素到顶端的距离
import java.util.Stack;

public class StackTest {

    static class Person{

        int id;
        String name;
        int age;

        public Person(int id, String name, int age){
            this.id = id;
            this.name = name;
            this.age = age;

        }

        public String toString(){
            return "id:"+ this.id + " " + "name:" + this.name +" " + "age:" + this.age;

        }

    }

    public static void main(String[] args) {

        Stack stack = new Stack();

        stack.push(new Person(1,"赵一",18));
        stack.push(new Person(2,"王二",19));
        stack.push(new Person(3,"张三",20));
        stack.push(new Person(4,"李四",21));

        System.out.println("栈顶元素是:(" + stack.peek() + ")");

        System.out.println("目标元素离栈顶多少距离:" + stack.search(stack.get(0)));

        System.out.println("栈元素从栈顶到栈底的排序是:");

        //此处先用size保存是因为pop时,size会减1,
        // 如果直接stack.size放在循环中比较,只能打印一半对象
        int size = stack.size();

        for (int i = 0; i < size ; i++) {

            Person p = (Person) stack.pop();

            System.out.println(p.toString());

        }

    }

}

图四

HashMap

数组的特点是:寻址容易,插入和删除困难;

而链表的特点是:寻址困难,插入和删除容易。

哈希表结合了两者的优点。

哈希表有多种不同的实现方法,可以理解将此理解为“链表的数组”

图五