一,集合

1.集合和数组的区别

数组长度是固定的,集合长度是可变的;数组中可以窜出基本数据类型,集合只能存储对象;数组中存储数据类型是单一的,集合中可以存储任意类型的对象。

2.集合框架体系

java 集合中修改元素的方式_arraylist

java 集合中修改元素的方式_System_02

List:有序存储,可重复
——ArrayList:数组实现,查找块,增删慢
——LinkedList:链表实现,增删块,查找慢
——Vector:和ArrayList原理相同,但线程安全,效率略低
Set:无存储顺序,不可重复
——HashSet:按Hash算法存储集合中的元素,有很好的存取和查找性能,不同步
——LinkedHashSet:根据元素的hashCode决定元素存储位置,同时使用链表维护元素的次序,按照元素添加顺序访问集合元素
——TreeSet:采用红黑树的数据结构存储集合元素,根据元素实际值的大小进行排序
——EnumSet:以枚举值在Enum类内的定义顺序决定集合元素的顺序,在内部以位向量的形式存储
3.总结,什么时候该使用什么样的集合
List:我们需要存储顺序,并且保留重复元素;如果查询较多,使用ArrayList;如果存取较多,使用LinkedList;如果需要线程安全,使用Vector
Set:不需要保留存储顺序,并且需要去掉重复元素;如果需要将元素排序,使用TreeSet;如果不需要排序,使用HashSet,HashSet比TreeSet效率高;如果需要保留存储顺序,又要过滤重复元素,使用LinkedHashSet

二,集合类
1.Collection接口共性方法

package Collection;
import java.util.*;

public class CollectionTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
//      增加,输出
//      [Spring, Summer, Autumn]
//      [Winter, Spring, Summer, Autumn, January]   
        Collection list=new ArrayList();
        list.add("Spring");
        list.add("Summer");
        list.add("Autumn");
        System.out.println(list);

        Collection list2=new ArrayList();
        list2.add("Winter");
        list2.addAll(list);
        list2.add("January");
        System.out.println(list2);

//      删除,输出
//      true
//      [Winter, Spring, Summer, Autumn]
//      true
//      [Winter]
        boolean remove=list2.remove("January");
        System.out.println(remove);
        System.out.println(list2);

        boolean removeAll=list2.removeAll(list);
        System.out.println(removeAll);
        System.out.println(list2);

//      修改
//      [Winter, Spring, Summer, Autumn, January]
//      []
        System.out.println(list2);
        list2.clear();
        System.out.println(list2);

//      判断
//      false
//      true
        boolean empty=list.isEmpty();
        System.out.println(empty);
        Boolean contains=list2.contains("January");
        System.out.println(contains);

//      获取长度
        System.out.println(list2.size());

    }

}

注意:以上代码时对增加、删除、修改、判断和获取等方法的示例,实际运行时需要注释掉部分代码。

2.List
判断两个对象相等,只要通过equals方法比较返回true即可
如下代码,仅以增加作为示例

package Collection;
import java.util.*;
/*
 * list集合特有的方法
 * 1.增加:void add(int index,E element)指定位置添加元素
 *       boolean addAll(int index,Collection c)指定位置添加集合
 * 2.删除:E remove(int index)删除至东京位置元素
 * 3.修改:E set(int index,E element)返回的是要替换的集合中的元素
 * 4.查找:E get(int index)
 *      int indexOf(Object o) 找不到返回-1
 *      lastIndexOf(Object o)
 * 5.求子集合:List<E> subList(int fromIndex, int toIndex)不包含toIndex
 */
public class ListTest {
    public static void main(String [] args){
        List list=new ArrayList();
        list.add("Spring");
        list.add("Summer");
        list.add("Autumn");

        System.out.println(list);//[Spring, Summer, Autumn]

        list.add(0,"Winter");
        System.out.println(list);//[Winter, Spring, Summer, Autumn]

        List list2=new ArrayList();
        list2.add("January");
        list2.add("February");
        list2.add("March");
        System.out.println(list2);//[January, February, March]
        boolean addAll=list2.addAll(1,list);
        System.out.println(addAll);//true
//      [January, Winter, Spring, Summer, Autumn, February, March]
        System.out.println(list2);
    }

}

3.ArrayList:数组实现,查找快,增删慢
数组的内存空间地址是连续的,ArrayList底层维护了一个Object[]用于存储对象,默认数组长度是10,可以通过new ArrayList(20)显式指定用于存储对象的数组的长度。数组可以直接按索引查找,所以较快;在增删时会牵扯到数组增容以及拷贝元素,所以慢。
Eg:去除ArrayList集合中重复元素
注意:自定义对象要进行复写toString和equals方法,因为Object是Person的父类,Object中toString返回的是哈希值,equals比较的是对象的地址值
原理:循环遍历该集合,每取出一个放置在行的集合中,放置之前先判断新集合是否已经包含了新的元素

package Collection;
import java.util.*;

class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {
        return this.name.hashCode() + age * 37;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Person)) {
            return false;
        }
        Person p = (Person) obj;

        return this.name.equals(p.name) && this.age == p.age;
    }

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

}

public class ArrayListTest {
    public static void main(String [] args){
        ArrayList arr = new ArrayList();
        Person p1 = new Person("jack", 20);
        Person p2 = new Person("rose", 18);
        Person p3 = new Person("rose", 18);
        arr.add(p1);
        arr.add(p2);
        arr.add(p3);
        System.out.println(arr);

        ArrayList arr2 = new ArrayList();
        for(int i=0;i<arr.size();i++){
            Object obj=arr.get(i);
            Person p=(Person)obj;
            if(!(arr2.contains(p))){
                arr2.add(p);
            }
        }
        System.out.println(arr2);

    }

}

4.LinkedList:链表实现,增删块,查找慢

package Collection;
import java.util.*;
/*
 * 特有的方法
 * addFirst(E e) addLast(E e) getFirst() getLast() 
 * removeFirst() removeLast() 
 * 如果集合中没有元素,获取或者删除元素会抛出异常NoSuchElementException
 * 数据结构
 * 栈:先进后出push() pop()
 * 队列:先进先出offer() poll()
 * descendingIterator()   返回逆序的迭代器对象
 */
public class LinkedListTest {
    public static void main(String [] args){
        LinkedList list = new LinkedList();
        list.add("西游记");
        list.add("三国演义");
        list.add("石头记");
        list.add("水浒传");
        list.add("全球通史");
        list.addFirst("史记");
        list.addLast("呐喊");
        // list.addFirst(null);
        // list.addLast(null);
        System.out.println(list);
        // 获取指定位置处的元素。
        String str = (String) list.get(0);
        System.out.println(str);
        // 返回此列表的第一个元素。
        String str2 = (String) list.getFirst();
        System.out.println(str2);
        System.out.println(str.equals(str2));

        // 获取指定位置处的元素。
        String str3 = (String) list.get(list.size() - 1);
        // 返回此列表的最后一个元素。
        String str4 = (String) list.getLast();
        System.out.println(str3.equals(str4));

        // 获取但不移除此列表的头(第一个元素)。
        Object element = list.element();
        System.out.println(element);

        int size = list.size();
        System.out.println(size);

    }
}
/*
[史记, 西游记, 三国演义, 石头记, 水浒传, 全球通史, 呐喊]
史记
史记
true
true
史记
7
*/

练习:使用集合模拟队列(先进先出)或者堆栈(后进先出)数据结构,以堆栈为例

package Collection;
import java.util.*;

public class ZhanTest {
    public static void main(String [] args){
        LinkedList list = new LinkedList();
        // 压栈,先进后出
        list.push("西游记");
        list.push("三国演义");
        list.push("石头记");
        list.push("水浒传");
        System.out.println(list);
        // 弹栈
        String str1 = (String) list.pop();
        System.out.println(str1);
        String str2 = (String) list.pop();
        System.out.println(str2);
        String str3 = (String) list.pop();
        System.out.println(str3);
        String str4 = (String) list.pop();
        System.out.println(str4);
        System.out.println(list.size());// 0
        System.out.println(list); //[]

    }
}
/*
[水浒传, 石头记, 三国演义, 西游记]
水浒传
石头记
三国演义
西游记
0
[]
*/

5.Vector:描述的是一个线程安全的ArrayList

package Collection;
import java.util.*;

public class VectorTest {
    public static void main(String [] args){
        Vector v = new Vector();
        v.addElement("aaa");
        v.addElement("bbb");
        v.addElement("ccc");
        System.out.println( v );
        System.out.println( v.elementAt(2) );   // ccc
        // 遍历Vector遍历
        Enumeration ens = v.elements();
        while ( ens.hasMoreElements() )
        {
            System.out.println( ens.nextElement() );
        }   

    }
}
/*
[aaa, bbb, ccc]
ccc
aaa
bbb
ccc
 */

6.Set:用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复
如果想要两个不同的Person对象视为相等的,必须覆盖Object继承下来的hashCode方法和equals方法。
判断两个元素相等的标准是,两个对象通过equals方法比较相等且hashCode返回值也相等。

7.HashSet:Set接口的典型实现,不能保证元素的排列顺序,不是同步的,线程不安全,存取速度快
HashSet存储元素的顺序并不是按照存入时的顺序,是按照哈希值来存的,所以取数据也是按照哈希值取的。

HashSet不能存入重复元素,那么HashSet如何检查重复?
当你试图把对象加入HashSet时,HashSet会使用对象的hashCode来判断对象加入的位置。同时也会与其他已经加入的对象的hashCode进行比较,如果没有相等的hashCode,HashSet就会假设对象没有重复出现。简单一句话,如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的。因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。如果元素(对象)的hashCode值相同,是不是就无法存入HashSet中了? 当然不是,会继续使用equals 进行比较.如果 equals为true 那么HashSet认为新加入的对象重复了,所以加入失败。如果equals 为false那么HashSet 认为新加入的对象没有重复.新元素可以存入.

总结概括起来就是:元素的哈希值是通过元素的hashcode方法 来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。

8.LinkedHashSet
HashSet的一个子类,根据元素的hashCode来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet会按元素的添加顺序来访问集合里的元素。
LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能,因为它以链表来维护内部顺序。

package Collection;
import java.util.*;

public class LinkedHashSetTest {

public static void main(String[] args) {
// TODO Auto-generated method stub
LinkedHashSet books = new LinkedHashSet();
books.add("疯狂Java讲义");
books.add("Spring实战");
System.out.println(books);
// 删除 疯狂Java讲义
books.remove("疯狂Java讲义");
// 重新添加 疯狂Java讲义
books.add("疯狂Java讲义");
System.out.println(books);
}
}
/*
[疯狂Java讲义, Spring实战]
[Spring实战, 疯狂Java讲义]
 */

9.TreeSet:采用红黑树的数据结构存储集合元素,根据元素实际值的大小进行排序

package Collection;
import java.util.*;

public class TreeSetTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TreeSet nums = new TreeSet();
        // 向TreeSet中添加四个Integer对象
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        // 输出集合元素,看到集合元素已经处于排序状态
        System.out.println(nums);
        // 输出集合里的第一个元素
        System.out.println(nums.first()); // 输出-9
        // 输出集合里的最后一个元素
        System.out.println(nums.last());  // 输出10
        // 返回小于4的子集,不包含4
        System.out.println(nums.headSet(4)); // 输出[-9, 2]
        // 返回大于5的子集,如果Set中包含5,子集中还包含5
        System.out.println(nums.tailSet(5)); // 输出 [5, 10]
        // 返回大于等于-3,小于4的子集。
        System.out.println(nums.subSet(-3 , 4)); // 输出[2]
    }
}
/*
[-9, 2, 5, 10]
-9
10
[-9, 2]
[5, 10]
[2]
 */

TreeSet支持两种排序方法:自然排序和定制排序,默认情况下,采用自然排序。
(1)自然排序,元素自身具备比较性,元素需要实现Comparable接口,覆盖compareTo方法,集合元素按照升序排序。这也就是为什么TreeSet存入字符串时,字符串默认输出是按升序排列的,因为字符串实现了Comparable接口并重写了compareTo方法,所以String对象具备了比较性。

package test;
import java.util.*;

class Person implements Comparable{
    private String name;
    private int age;
    private String gender;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int hashCode() {
        return name.hashCode() + age * 37;
    }

    public boolean equals(Object obj) {
        System.err.println(this + "equals :" + obj);
        if (!(obj instanceof Person)) {
            return false;
        }
        Person p = (Person) obj;
        return this.name.equals(p.name) && this.age == p.age;

    }

    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", gender=" + gender
                + "]";
    }

    public int compareTo(Object obj) {

        Person p = (Person) obj;
        System.out.println(this+" compareTo:"+p);
        if (this.age > p.age) {
            return 1;
        }
        if (this.age < p.age) {
            return -1;
        }
        return this.name.compareTo(p.name);
    }
}
public class TreeSetTest{

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TreeSet ts = new TreeSet();
        ts.add(new Person("aa", 20, "男"));
        ts.add(new Person("bb", 18, "女"));
        ts.add(new Person("cc", 17, "男"));
        ts.add(new Person("dd", 17, "女"));
        ts.add(new Person("dd", 15, "女"));
        ts.add(new Person("dd", 15, "女"));


        System.out.println(ts);
        System.out.println(ts.size()); // 5
    }

}

(2)定制排序,当元素自身不具备比较性,或者元素吱声比较性不是所需的时候,使用定制排序。定义一个类实现Comparator接口,覆盖compare方法,并将该接口的子类对象作为参数传递给TreeSet集合的构造函数。由于Comparator是一个函数式接口,因此可以用Lambda表达式代替Comparator对象。

package Collection;
import java.util.*;

class M
{
    int age;
    public M(int age)
    {
        this.age = age;
    }
    public String toString()
    {
        return "M[age:" + age + "]";
    }
}
public class TreeSetTest4
{
    public static void main(String[] args)
    {
        // 此处Lambda表达式的目标类型是Comparator
        TreeSet ts = new TreeSet((o1 , o2) ->
        {
            M m1 = (M)o1;
            M m2 = (M)o2;
            // 根据M对象的age属性来决定大小,age越大,M对象反而越小
            return m1.age > m2.age ? -1
                : m1.age < m2.age ? 1 : 0;
        });
        ts.add(new M(5));
        ts.add(new M(-3));
        ts.add(new M(9));
        System.out.println(ts);
    }
}
/*
[M[age:9], M[age:5], M[age:-3]]
*/

小练习:将字符串中的数值进行排序,可以使用TreeSet完成,因为TreeSet自身具备排序功能。

package Collection;
import java.util.*;

public class TreeSetEx {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str = "8 10 15 5 2 7";
        String[] strs = str.split(" ");
        TreeSet ts = new TreeSet();
        for (int x = 0; x < strs.length; x++) {
            int y = Integer.parseInt(strs[x]);
            ts.add(y);
        }
        System.out.println(ts);
    }

}
/*
[2, 5, 7, 8, 10, 15]
*/

三,迭代器
为了方便处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素,例如删除和获取集合中的元素,该对象就叫做迭代器(Iterator)。
1.Iterable
jdk1.5之后添加的新接口,Collection的父接口,实现了Iterable的类就是可迭代的,并支持增强for循环。该接口只有一个方法即获取迭代器的方法iterable(),可以获取每个容器自身的迭代器Iterator。
2.Iterator
Iterator主要用于遍历(即迭代访问)Collection集合中的元素,该接口隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的同一编程接口,Iterator接口定义了如下4个方法:
boolean hasNext(): 如果被迭代的集合元素还没有被遍历完,返回true
Object next(): 返回集合里的下一个元素
void remove(): 删除集合里上一次next方法返回的元素
void forEachRemaining(Consumer action): java8为Iterator新增的默认方法,该方法可使用Lambda表达式遍历集合元素
3.迭代器遍历的几个例子

while循环遍历集合

package Collection;
import java.util.*;
/*
 * while循环
 */
public class IteratorWhile {
    public static void main(String []args){
        ArrayList list=new ArrayList();

        list.add("计算机网络");
        list.add("现代操作系统");
        list.add("java编程思想");
        list.add("java核心技术");
        list.add("java语言程序设计");
        System.out.println(list);

        Iterator it=list.iterator();
        while(it.hasNext()){
            String next=(String)it.next();
            System.out.println(next);
        }
    }
}
/*
[计算机网络, 现代操作系统, java编程思想, java核心技术, java语言程序设计]
计算机网络
现代操作系统
java编程思想
java核心技术
java语言程序设计
 */

利用for循环遍历集合

package Collection;
import java.util.*;
/*
 * for循环
 * 需要去除全部元素时,可以通过循环,java建议使用for循环,因为可以对内存进行一下优化
 */
public class IteratorWhile {
    public static void main(String []args){
        ArrayList list=new ArrayList();

        list.add("计算机网络");
        list.add("现代操作系统");
        list.add("java编程思想");
        list.add("java核心技术");
        list.add("java语言程序设计");
        System.out.println(list);

        for(Iterator it=list.iterator();it.hasNext();){
            String next=(String)it.next();
            System.out.println(next);
        }
    }
}
/*
[计算机网络, 现代操作系统, java编程思想, java核心技术, java语言程序设计]
计算机网络
现代操作系统
java编程思想
java核心技术
java语言程序设计
 */

使用迭代器清空集合,注意两个细节:
(1)如果迭代器的指针已经指向了集合的末尾,那么如果再调用next()会返回NoSuchElementException异常
(2)如果调用remove之前没有调用next是不合法的,会抛出IllegalStateException异常,remove方法会返回上次调用next方法时返回的元素,如果想要删除指定位置上的元素,需要越过这个元素,例如,下面是如何删除字符串集合中第一个元素的方法

Iterator<String> it=c.iterator();
it.next();
it.remove();

如果想要删除两个相邻的元素,不能直接这样

it.remove();
it.remove();//Error!

相反的,必须先调用next越过将要删除的元素

it.remove();
it.next();
it.remove();

使用一张图可能会更好地理解

java 集合中修改元素的方式_java_03

package Collection;
import java.util.*;

public class IteratorTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArrayList list=new ArrayList();
        list.add("Spring");
        list.add("Summer");
        list.add("Autumn");
        list.add("Winter");
        System.out.println(list);

        Iterator it=list.iterator();
        while(it.hasNext()){
            it.next();
            it.remove();
        }
        System.out.println(list);

//      String next=(String)it.next();
//      System.out.println(next);

//      while(it.hasNext()){
//          String next2=(String)it.next();
//          System.out.println(next2);
//      }
    }

}

4.List特有的迭代器ListIterator
如果是List集合,想要在迭代中操作元素可以使用List集合特有的迭代器ListIterator,该迭代器支持在迭代过程中,添加元素和修改元素。

package Collection;
import java.util.*;
/*
 * 特有的方法
 * add 将制定的元素插入列表,该元素直接插入到next返回的下一个元素的前面
 * set 用制定元素替换next或previous返回的最后一个元素
 * hasPrevious 逆向遍历列表
 * previous 返回列表的前一个元素
 */
public class LinkedListTest {
    public static void main(String [] args){
        LinkedList list = new LinkedList();
        list.add("西游记");
        list.add("三国演义");
        list.add("石头记");
        list.add("水浒传");

        System.out.println(list);

        ListIterator lit=list.listIterator();
        while(lit.hasNext()){
            String next=(String)lit.next();
            System.out.println(next);
        }

        while(lit.hasPrevious()){//倒序遍历
            String previous=(String)lit.previous();
            System.out.println(previous);
        }

        System.out.println(list);
        lit.next();
        lit.next();
        System.out.println(lit.next());
        lit.add("平凡的世界");
        //add方法将指定元素插入 列表,该元素直接插入到next返回的元素后面
        System.out.println(list);
    }
}
/*
[西游记, 三国演义, 石头记, 水浒传]
西游记
三国演义
石头记
水浒传
水浒传
石头记
三国演义
西游记
[西游记, 三国演义, 石头记, 水浒传]
石头记
[西游记, 三国演义, 石头记, 平凡的世界, 水浒传]
*/

集合未完……