集合框架

时间:2019-06-12 21:21:30   收藏:0   阅读:47

集合框架
2.1. Collection
java提供了一种可以存一组数据的数据结构,其提供了丰富的方法,在实际开发中往往比数组使用的广泛。这种数据结构称为集合:Collection。
Collection是一个接口,其定义了集合的相关功能方法。
2.1.1. List和Set
Collection派生出了两个子接口,一个是List另一个则是Set。
List:称为可重复集,顾名思义,该集合中是允许存放重复元素的,那么何为重复元素?重复元素指的并非是同一个元素,而是指equals方法比较为true的元素。
Set:称为不可重复集,所以,该集合中是不能将相同的元素存入集合两次,同List,这里相同指的也是两个元素的equals比较结果为true。
2.1.2. 集合持有对象的引用
集合中存储的都是引用类型的元素,那么引用类型变量实际上存储的是对象的“地址”,所以实际上集合只存储了元素对象在堆中的地址。而并不是将对象本身存入了集合中。
2.1.3. add()方法
Collection定义了一个add方法用于向集合中添加新元素。 该方法定义为:
1.boolean add(E e)
该方法会将给定的元素添加进集合,若添加成功则返回true,否则返回false
例如:
1.Collection c = new ArrayList();
2.System.out.println(c); // []
3.c.add("a");
4.c.add("b");
5.c.add("c");
6.System.out.println(c); // [a, b, c]
这里Collection后面的是泛型,我们后面会讲解,这里不写也没有关系。
2.1.4. contains方法
1.boolean contains(Object o)
该方法会用于判断给定的元素是否被包含在集合中。若包含则返回true,否则返回false。
这里需要注意的是,集合在判断元素是否被包含在集合中是使用元素的equals的比较结果。
(o==null???e==null?:?o.equals(e)) 其中e是集合中的元素。
例如:
1.Collection cells = new ArrayList();
2.cells.add(new Cell(1, 2));
3.cells.add(new Cell(1, 3));
4.cells.add(new Cell(2, 2));
5.cells.add(new Cell(2, 3));????
6.Cell cell = new Cell(1, 3);
7.// List集合contains方法和对象的equals方法相关
8.boolean flag = cells.contains(cell);
9.// 如果Cell不重写equals方法将为false
10.System.out.println(flag); // true
2.1.5. size,clear,isEmpty方法
size方法用于获取当前集合中的元素总数。该方法定义为:int size()
clear方法用于清空集合。该方法定义为:void clear()
isEmpty方法用于判断当前集合中是否不 包含元素。该方法定义为:boolean isEmpty()
例如:
1.Collection c = new HashSet();
2.System.out.println(c.isEmpty()); // true
3.c.add("java");????
4.c.add("cpp");????
5.c.add("php");
6.c.add("c#");????
7.c.add("objective-c");

  1. 9.// isEmpty:false, size: 5
    10.System.out.println("isEmpty:" + c.isEmpty() + ",size: " + c.size());????????
    11.c.clear();
  2. 13.// isEmpty:true, size: 0
    14.System.out.println("isEmpty:" + c.isEmpty() + ", size: " + c.size());????
    addAll与containsAll方法
    集合也提供了批处理操作:
    addAll方法用于将给定集合中的所有元素添加到当前集合中,其方法定义为:
    1.boolean addAll(Collection c)
    例如:
    1.Collection c1 = new ArrayList();
    2.c1.add("java");
    3.c1.add("cpp");????????
    4.c1.add("php");
    5.c1.add("c#");????????
    6.c1.add("objective-c");
    7.System.out.println(c1); // [java, cpp, php, c#, objective-c]
    8.Collection c2 = new HashSet();
    9.c2.addAll(c1);
    10.System.out.println(c2); // [cpp, php, c#, java, objective-c]
    containsAll方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回true。其方法定义为:
    1.boolean containsAll(Collection c)
    例如:
    1.Collection c1 = new ArrayList();
    2.c1.add("java");????????
    3.c1.add("cpp");????????
    4.c1.add("php");
    5.c1.add("c#");????????
    6.c1.add("objective-c");
    7.System.out.println(c1); // [java, cpp, php, c#, objective-c]
    8.Collection c2 = new ArrayList();
    9.c2.add("java");????????
    10.c3.add("cpp");
    11.System.out.println(c1.containsAll(c2)); // true
    1.2. Iterator 迭代器
    Collection提供了一个遍历集合的通用方式,迭代器(Iterator)。
    获取迭代器的方式是使用Collection定义的方法:
    1.Iterator iterator()
    迭代器Iterator是一个接口,集合在覆盖Collection的iterator()方法时提供了迭代器的实现。
    Iterator提供了统一的遍历集合元素的方式。
    1.2.1. hasNext与next方法
    迭代器用于遍历集合的两个主要方法:
    ?boolean hasNext():判断集合是否还有元素可以遍历。
    ?E next():返回迭代的下一个元素
    遍历集合应遵循“先问后取”的方式,也就是说,应当在确定hasNext()方法的返回值为true的情况下再通过next()方法取元素。
    由此可以看出,使用迭代器遍历集合是通过boolean值驱动的,所以它更适合使用while循环来遍历。
    例如:
    1.Collection c = new HashSet();
    2.c.add("java");????????
    3.c.add("cpp");????????
    4.c.add("php");
    5.c.add("c#");????????
    6.c.add("objective-c");
    7.Iterator it = c.iterator();
    8.while (it.hasNext()) {
    9.????String str = it.next();
    10.????System.out.println(str);
    11.}
    1.2.2. remove方法
    迭代器还提供了一个方法:void remove()。该方法用于删除迭代器当次从集合中获取的元素。若我们在迭代过程中想删除集合元素时,我们就需要通过该方法来进行。这里需要注意,在使用迭代器遍历集合时是不能通过集合自身提供的remove方法删除元素的,否则迭代器在迭代时会抛出异常。
    例如:
    1.Collection c = new HashSet();
    2.c.add("java");????????
    3.c.add("cpp");????????
    4.c.add("php");
    5.c.add("c#");????????
    6.c.add("objective-c");
    7.System.out.println(c); // [cpp, php, c#, java, objective-c]
    8.Iterator it = c.iterator();
    9.while (it.hasNext()) {
    10.????String str = it.next();
    11.????if (str.indexOf("c") != -1) {
    12.????????it.remove();
    13.????}
    14.}
    15.System.out.println(c); // [php, java]????
    1.2.3. 增强for循环
    Java5.0之后推出了一个新的特性,增强for循环,也称为新循环。该循环不通用于传统循环的工作,其只用于便利集合或数组。 语法:
    1.for(元素类型 e : 集合或数组){
    2.????循环体
    3.}
    新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式。所以新循环本质上是迭代器。
    例如:
    1.Collection c = new HashSet();
    2.c.add("java");
    3.c.add("cpp");
    4.c.add("php");
    5.c.add("c#");
    6.c.add("objective-c");
    7.for (String str : c) {
    8.????System.out.print(str.toUpperCase() + " ");
    9.}
    10.// CPP PHP C# JAVA OBJECTIVE-C
    1.3. 泛型机制
    1.3.1. 泛型在集合中的应用
    泛型是Java SE 5.0引入的特性,泛型的本质是参数化类型。在类、接口和方法的定义过程中,所操作的数据类型被传入的参数指定。
    Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中的对象类型。Java编译器可以据此进行类型检查,这样可以减少代码在运行时出现错误的可能性。
    我们来举个例子,比如ArrayList,其在定义时是这样的:
    1.public class ArrayList {
    2.????… … …????????????????
    3.????public boolean add(E e) {…};
    4.????public E get(int index) {…};
    5.}
    由此我们可以看出,再声明ArrayList时,类名的右侧有一个。"<>"表示泛型,而其中可以使用数字字母下划线(数字不能是第一个字符)来表示泛型的名字。(通常我们使用一个大写字母来表示,当然这个不是规定。)这时,在类中声明的方法的参数,返回值类型可以被定义为泛型。这样在创建对象时可以将类型作为参数传递,此时,类定义所有的E将被替换成传入的参数。
    例如:
    1.ArrayList list = new ArrayList();//泛型E在这里被指定为String类型
    2.list.add("One");//那么add方法的参数就被替换为String类型
    3.list.add(100);//这里就会出现编译错误,因为这里的参数应为String类型。
  3. 集合操作——线性表
    2.1. List
    List接口是Collection的子接口,用于定义线性表数据结构;可以将List理解为存放对象的数组,只不过其元素个数可以动态的增加或减少。并且List是可重复集,这个我们在以前的章节已经描述。
    2.1.1. ArrayList和LinkedList
    List接口的两个常见实现类为ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。
    可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适合于随机访问而LinkedList更适合于插入和删除;在性能要求不是特别苛刻的情形下可以忽略这个差别。
    2.1.2. get与set方法
    List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下标的get和set方法。
    ?E get(int index):获取集合中指定下标对应的元素,下标从0开始。
    ?E set(int index, E elment):将给定的元素存入给定位置,并将原位置的元素返回。
    例如:
    1.List list = new ArrayList();
    2.list.add("java");????????
    3.list.add("cpp");????????
    4.list.add("php");
    5.list.add("c#");????????
    6.list.add("objective-c");
    7.// get方法遍历List
    8.for (int i = 0; i < list.size(); i++) {
    9.????System.out.println(list.get(i));
    10.}
    11.String value = list.set(1, "c++");
    12.System.out.println(value); // cpp
    13.System.out.println(list); // [java, c++, php, c#, objective-c]
    14.// 交换位置1和3上的元素
    15.list.set(1, list.set(3, list.get(1)));
    16.System.out.println(list);
    17.// [java, c#, php, c++, objective-c]
    2.1.3. 插入和删除
    List根据下标的操作还支持插入与删除操作:
    1.void add(int index,E element):
    将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。
    1.E remove(int index):
    删除给定位置的元素,并将被删除的元素返回。
    例如:
    1.List list = new ArrayList();
    2.list.add("java");
    3.list.add("c#");
    4.System.out.println(list); // [java, c#]
    5.???? ????
    6.list.add(1, "cpp");
    7.System.out.println(list); // [java, cpp, c#]
    8.???? ????
    9.list.remove(2);
    10.System.out.println(list); // [java, cpp]
    2.1.4. subList方法
    List的subList方法用于获取子List。
    需要注意的是,subList获取的List与原List占有相同的存储空间,对子List的操作会影响的原List。
    1.List subList(int fromIndex, int toIndex);
    fromIndex和toIndex是截取子List的首尾下标(前包括,后不包括) 。
    例如:
    1.List list = new ArrayList();
    2.for (int i = 0; i < 10; i++) {
    3.????list.add(i);
    4.}
    5.System.out.println(list); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    6.List subList = list.subList(3, 8);
    7.System.out.println(subList); // [3, 4, 5, 6, 7]
    8.// subList获得的List和源List占有相同的数据空间
    9.for (int i = 0; i < subList.size(); i++) {
    10.????subList.set(i, subList.get(i) * 10);
    11.}
    12.System.out.println(subList); // [30, 40, 50, 60, 70]
    13.System.out.println(list); // [0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
    14.// 可以用于删除连续元素list.subList(3, 8).clear();
    15.System.out.println(list);
    2.1.5. List转换为数组
    List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
    其有两个方法:
    1.Object[] toArray()
  4. 3.T[] toArray(T[] a)
    其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。
    例如:
    1.List list = new ArrayList();
    2.list.add("a");
    3.list.add("b");
    4.list.add("c");
  5. 6.//通常我们传入的数组不需要给定长度
    7.String[] strArr = list.toArray(new String[] {});????????System.out.println(Arrays.toString(strArr)); // [a, b, c]
    2.1.6. 数组转换为List
    Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。
    其方法定义为:
    1.static List asList<T… a>
    返回的List的集合元素类型由传入的数组的元素类型决定。
    需要注意的是,返回的集合我们不能对其增删元素,否则会抛出异常。并且对集合的元素进行的修改会影响数组对应的元素。
    例如:
    1.String[] strArr = { "a", "b", "c" };
    2.List list = Arrays.asList(strArr);
    3.System.out.println(list); // [a, b, c]
    4.// list.add("d"); // 会抛出UnsupportedOperationException
  6. 6.// ????java.util.Arrays$ArrayList
    7.System.out.println(list.getClass().getName());????????
    8.List list1 = new ArrayList();
    9.list1.addAll(Arrays.asList(strArr));
    2.2. List排序
    2.2.1. Collections.sort方法实现排序
    Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。该方法的定义为:
    1.void sort(List list)
    其作用是对集合元素进行自然排序(按照元素的由小至大的顺序)
    例如:
    1.List list = new ArrayList();
    2.Random r = new Random(1);
    3.for (int i = 0; i < 10; i++) {
    4.????list.add(r.nextInt(100));
    5.}
    6.System.out.println(list); // [85, 88, 47, 13, 54, 4, 34, 6, 78, 48]
    7.Collections.sort(list);
    8.System.out.println(list); // [4, 6, 13, 34, 47, 48, 54, 78, 85, 88]
    2.2.2. Comparable
    通过上一节我们知道了如何对集合元素进行自然排序,但是要想对元素进行自然排序那么就必须要有一个必要条件,就是元素的大小。集合中存入的都是引用类型,是以对象的形式存在于内存中,那么对象是如何进行的大小比较呢?实际上,若想对某个集合的元素进行自然排序,该集合的元素有一个要求,就是这些元素必须是Comparable的子类。
    Comparable是一个接口,用于定义其子类是可以比较的。因为该接口有一个抽象方法:
    1.????int compareTo(T t)
    所有子类都需要重写该方法来定义对象间的比较规则。该方法要求返回一个整数,这个整数不关心具体的值,而是关注取值范围。
    ?当返回值>0时,表示当前对象比参数给定的对象大。
    ?当返回值<0时,表示当前对象比参数给定的对象小。
    ?当返回值=0时,表示当前对象和参数给定的对象相等。
    例如:
    1.Class Cell implements Comparable{
    2.????int row;
    3.????int col;
  7. 5.????public Cell(int row,int col){
    6.????????this.row = row;
    7.????????this.col = col;
    8.????}
    9.????????????
    10.????public int compareTo(Cell c){
    11.????????//根据row比较大小
    //如果当前的对象的row-传进来的row>0,说明当前对象比传进来的对象大
    12.????????return this.row - c.row;
    13.????}
    14.}
    那么Collections的sort在进行排序时就会根据集合中元素的compareTo方法的返回值来判断大小从而进行自然排序。
    1.// Cell实现了Comparable接口,CompareTo方法逻辑为按照row值的大小排序
    2.List cells = new ArrayList();
    3.cells.add(new Cell(2, 3));
    4.cells.add(new Cell(5, 1));
    5.cells.add(new Cell(3, 2));
    6.Collections.sort(cells);
    7.System.out.println(cells); // [(2,3), (3,2), (5,1)]
    2.2.3. comparator
    一旦Java类实现了Comparable,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。
    该接口要求实现类必须重写其定义的方法:
    1.????int compare(T o1,T o2)
    该方法的返回值要求,若o1>o2则返回值应>0,若o1<o2则返回值应<0,若o1==o2则返回值应为0
    例如:
    1.List cells = new ArrayList();
    2.cells.add(new Cell(2, 3));????
    3.cells.add(new Cell(5, 1));
    4.cells.add(new Cell(3, 2));
    5.// 按照col值的大小排序
    6.Collections.sort(cells, new Comparator() {
    [email protected]
    8.????public int compare(Cell o1, Cell o2) {
    9.????????return o1.col - o2.col;}
    10.????});
    11.System.out.println(cells); // [(5,1), (3,2), (2,3)]
    2.3. 队列和栈
    2.3.1. Queue
    队列(Queue)是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。
    队列遵循先进先出(FIFO First Input First Output )的原则。
    JDK中提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常要进行插入和删除的操作,而LinkedList在这方面效率较高)。
    Queue提供了操作队列的相关方法,其主要方法如下:
    ?boolean offer(E e):将元素追加到队列末尾,若添加成功则返回true。
    ?E poll():从队首删除并返回该元素。
    ?E peek():返回队首元素,但是不删除。
    例如:
    1.Queue queue = new LinkedList();
    2.queue.offer("a");
    3.queue.offer("b");
    4.queue.offer("c");
    5.System.out.println(queue); // [a, b, c]
    6.String str = queue.peek();
    7.System.out.println(str); // a
    8.while (queue.size() > 0) {
    9.????str = queue.poll();
    10.????System.out.print(str + " "); // a b c
    11.}
    2.3.2. Deque
    Deque是Queue的子接口,定义了所谓“双端队列”即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。
    如果将Deque限制为只能从一端入队和出队,则可实现“栈”(Stack)的数据结构,对于栈而言,入栈称之为push,出栈称之为pop。
    栈遵循先进后出(FILO First Input Last Output )的原则。
    Deque提供了操作栈的相关方法,其主要方法如下:
    ?void push(E e):将给定元素"压入"栈中。存入的元素会在栈首。即:栈的第一个元素
    ?E pop():将栈首元素删除并返回。
    例如:
    1.Deque stack = new LinkedList();
    2.stack.push("a");
    3.stack.push("b");
    4.stack.push("c");
    5.System.out.println(stack); // [c, b, a]
    6.String str = stack.peek();
    7.System.out.println(str); // c
    8.while (stack.size() > 0) {
    9.????str = stack.pop();
    10.????System.out.print(str + " "); // c b a
    11.}
    ?List
    List接口是Collection的子接口,用于定义线性表数据结构;可以将List理解为存放对象的数组,只不过其元素个数可以动态的增加或减少。并且List是可重复集,这个我们在以前的章节已经描述。
    2.1.1. ArrayList和LinkedList
    List接口的两个常见实现类为ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。
    可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适合于随机访问而LinkedList更适合于插入和删除;在性能要求不是特别苛刻的情形下可以忽略这个差别。
    2.1.2. get与set方法
    List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下标的get和set方法。
    ?E get(int index):获取集合中指定下标对应的元素,下标从0开始。
    ?E set(int index, E elment):将给定的元素存入给定位置,并将原位置的元素返回。
    例如:
    1.List list = new ArrayList();
    2.list.add("java");????????
    3.list.add("cpp");????????
    4.list.add("php");
    5.list.add("c#");????????
    6.list.add("objective-c");
    7.// get方法遍历List
    8.for (int i = 0; i < list.size(); i++) {
    9.????System.out.println(list.get(i));
    10.}
    11.String value = list.set(1, "c++");
    12.System.out.println(value); // cpp
    13.System.out.println(list); // [java, c++, php, c#, objective-c]
    14.// 交换位置1和3上的元素
    15.list.set(1, list.set(3, list.get(1)));
    16.System.out.println(list);
    17.// [java, c#, php, c++, objective-c]
    2.1.3. 插入和删除
    List根据下标的操作还支持插入与删除操作:
    1.void add(int index,E element):
    将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。
    1.E remove(int index):
    删除给定位置的元素,并将被删除的元素返回。
    例如:
    1.List list = new ArrayList();
    2.list.add("java");
    3.list.add("c#");
    4.System.out.println(list); // [java, c#]
    5.???? ????
    6.list.add(1, "cpp");
    7.System.out.println(list); // [java, cpp, c#]
    8.???? ????
    9.list.remove(2);
    10.System.out.println(list); // [java, cpp]
    2.1.4. subList方法
    List的subList方法用于获取子List。
    需要注意的是,subList获取的List与原List占有相同的存储空间,对子List的操作会影响的原List。
    1.List subList(int fromIndex, int toIndex);
    fromIndex和toIndex是截取子List的首尾下标(前包括,后不包括) 。
    例如:
    1.List list = new ArrayList();
    2.for (int i = 0; i < 10; i++) {
    3.????list.add(i);
    4.}
    5.System.out.println(list); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    6.List subList = list.subList(3, 8);
    7.System.out.println(subList); // [3, 4, 5, 6, 7]
    8.// subList获得的List和源List占有相同的数据空间
    9.for (int i = 0; i < subList.size(); i++) {
    10.????subList.set(i, subList.get(i) * 10);
    11.}
    12.System.out.println(subList); // [30, 40, 50, 60, 70]
    13.System.out.println(list); // [0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
    14.// 可以用于删除连续元素list.subList(3, 8).clear();
    15.System.out.println(list);
    2.1.5. List转换为数组
    List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
    其有两个方法:
    1.Object[] toArray()
  8. 3.T[] toArray(T[] a)
    其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。
    例如:
    1.List list = new ArrayList();
    2.list.add("a");
    3.list.add("b");
    4.list.add("c");
  9. 6.//通常我们传入的数组不需要给定长度
    7.String[] strArr = list.toArray(new String[] {});????????System.out.println(Arrays.toString(strArr)); // [a, b, c]
    2.1.6. 数组转换为List
    Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。
    其方法定义为:
    1.static List asList<T… a>
    返回的List的集合元素类型由传入的数组的元素类型决定。
    需要注意的是,返回的集合我们不能对其增删元素,否则会抛出异常。并且对集合的元素进行的修改会影响数组对应的元素。
    例如:
    1.String[] strArr = { "a", "b", "c" };
    2.List list = Arrays.asList(strArr);
    3.System.out.println(list); // [a, b, c]
    4.// list.add("d"); // 会抛出UnsupportedOperationException
  10. 6.// ????java.util.Arrays$ArrayList
    7.System.out.println(list.getClass().getName());????????
    8.List list1 = new ArrayList();
    9.list1.addAll(Arrays.asList(strArr));
    2.2. List排序
    2.2.1. Collections.sort方法实现排序
    Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。该方法的定义为:
    1.void sort(List list)
    其作用是对集合元素进行自然排序(按照元素的由小至大的顺序)
    例如:
    1.List list = new ArrayList();
    2.Random r = new Random(1);
    3.for (int i = 0; i < 10; i++) {
    4.????list.add(r.nextInt(100));
    5.}
    6.System.out.println(list); // [85, 88, 47, 13, 54, 4, 34, 6, 78, 48]
    7.Collections.sort(list);
    8.System.out.println(list); // [4, 6, 13, 34, 47, 48, 54, 78, 85, 88]
    2.2.2. Comparable
    通过上一节我们知道了如何对集合元素进行自然排序,但是要想对元素进行自然排序那么就必须要有一个必要条件,就是元素的大小。集合中存入的都是引用类型,是以对象的形式存在于内存中,那么对象是如何进行的大小比较呢?实际上,若想对某个集合的元素进行自然排序,该集合的元素有一个要求,就是这些元素必须是Comparable的子类。
    Comparable是一个接口,用于定义其子类是可以比较的。因为该接口有一个抽象方法:
    1.????int compareTo(T t)
    所有子类都需要重写该方法来定义对象间的比较规则。该方法要求返回一个整数,这个整数不关心具体的值,而是关注取值范围。
    ?当返回值>0时,表示当前对象比参数给定的对象大。
    ?当返回值<0时,表示当前对象比参数给定的对象小。
    ?当返回值=0时,表示当前对象和参数给定的对象相等。
    例如:
    1.Class Cell implements Comparable{
    2.????int row;
    3.????int col;
  11. 5.????public Cell(int row,int col){
    6.????????this.row = row;
    7.????????this.col = col;
    8.????}
    9.????????????
    10.????public int compareTo(Cell c){
    11.????????//根据row比较大小
    12.????????return this.row - c.row;
    13.????}
    14.}
    那么Collections的sort在进行排序时就会根据集合中元素的compareTo方法的返回值来判断大小从而进行自然排序。
    1.// Cell实现了Comparable接口,CompareTo方法逻辑为按照row值的大小排序
    2.List cells = new ArrayList();
    3.cells.add(new Cell(2, 3));
    4.cells.add(new Cell(5, 1));
    5.cells.add(new Cell(3, 2));
    6.Collections.sort(cells);
    7.System.out.println(cells); // [(2,3), (3,2), (5,1)]
    2.2.3. comparator
    一旦Java类实现了Comparable,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。
    该接口要求实现类必须重写其定义的方法:
    1.????int compare(T o1,T o2)
    该方法的返回值要求,若o1>o2则返回值应>0,若o1<o2则返回值应<0,若o1==o2则返回值应为0
    例如:
    1.List cells = new ArrayList();
    2.cells.add(new Cell(2, 3));????
    3.cells.add(new Cell(5, 1));
    4.cells.add(new Cell(3, 2));
    5.// 按照col值的大小排序
    6.Collections.sort(cells, new Comparator() {
    [email protected]
    8.????public int compare(Cell o1, Cell o2) {
    9.????????return o1.col - o2.col;}
    10.????});
    11.System.out.println(cells); // [(5,1), (3,2), (2,3)]
    2.3. 队列和栈
    2.3.1. Queue
    队列(Queue)是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。
    队列遵循先进先出(FIFO First Input First Output )的原则。
    JDK中提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常要进行插入和删除的操作,而LinkedList在这方面效率较高)。
    Queue提供了操作队列的相关方法,其主要方法如下:
    ?boolean offer(E e):将元素追加到队列末尾,若添加成功则返回true。
    ?E poll():从队首删除并返回该元素。
    ?E peek():返回队首元素,但是不删除。
    例如:
    1.Queue queue = new LinkedList();
    2.queue.offer("a");
    3.queue.offer("b");
    4.queue.offer("c");
    5.System.out.println(queue); // [a, b, c]
    6.String str = queue.peek();
    7.System.out.println(str); // a
    8.while (queue.size() > 0) {
    9.????str = queue.poll();
    10.????System.out.print(str + " "); // a b c
    11.}
    2.3.2. Deque
    Deque是Queue的子接口,定义了所谓“双端队列”即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。
    如果将Deque限制为只能从一端入队和出队,则可实现“栈”(Stack)的数据结构,对于栈而言,入栈称之为push,出栈称之为pop。
    栈遵循先进后出(FILO First Input Last Output )的原则。
    Deque提供了操作栈的相关方法,其主要方法如下:
    ?void push(E e):将给定元素"压入"栈中。存入的元素会在栈首。即:栈的第一个元素
    ?E pop():将栈首元素删除并返回。
    例如:
    1.Deque stack = new LinkedList();
    2.stack.push("a");
    3.stack.push("b");
    4.stack.push("c");
    5.System.out.println(stack); // [c, b, a]
    6.String str = stack.peek();
    7.System.out.println(str); // c
    8.while (stack.size() > 0) {
    9.????str = stack.pop();
    10.????System.out.print(str + " "); // c b a
    11.}
    Map接口
    1.1.1. Map 接口
    java提供了一组可以以键值对(key-value)的形式存储数据的数据结构,这种数据结构成为Map。我们可以把Map看成一个多行两列的表格,其中第一列存放key,第二列存放value。
    而每一行就相当于一组key-value对,表示一组数据。
    Map对存入的元素有一个要求,就是key不能重复,所谓不能重复指的是在Map中不能包含两个equals为true的key。
    Map对于key,value的类型没有严格要求,只要是引用类型均可。但是为了保证在使用时不会造成数据混乱,通常我们会使用泛型去约束key与value的类型。
    1.1.2. put方法
    既然我们知道了Map在保存数据时实际上是存入了两部分信息的 ,key与value。那么我们来看看如何向Map中存入数据。
    Map提供了一个方法:
    1.V put(K k,V v)
    该方法的作用是将key-value对存入Map中,因为Map中不允许出现重复的key,所以若当次存入的key已经在Map中存在,则是替换value操作,而返回值则为被替换的元素。若此key不存在,那么返回值为null。
    1.1.3. get方法
    我们学会了如何向Map中存入数据,那么我们再来看看如何获取数据。Map中获取数据的方式是给定Key获取对应的Value。
    Map提供了一个方法:
    1.V get(Object key)
    该方法的作用就是根据给定的key去查找Map中对应的value并返回,若当前Map中不包含给定的key,那么返回值为null。
    1.1.4. containsKey方法
    Map中的containsKey方法用于检测当前Map中是否包含给定的key。其方法定义如下:
    1.boolean containsKey(Object key)
    若当前Map中包含给定的key(这里检查是否包含是根据key的equals比较结果为依据的。)则返回true。
    1.2. HashMap
    1.2.1. hash表原理
    HashMap是Map的一个常用的子类实现。其实使用散列算法实现的。
    HashMap内部维护着一个散列数组(就是一个存放元素的数组),我们称其为散列桶,而当我们向HashMap中存入一组键值对时,HashMap首先获取key这个对象的hashcode()方法的返回值,然后使用该值进行一个散列算法,得出一个数字,这个数字就是这组键值对要存入散列数组中的下标位置。
    那么得知了下标位置后,HashMap还会查看散列数组当前位置是否包含该元素。(这里要注意的是,散列数组中每个元素并非是直接存储键值对的,而是存入了一个链表,这个链表中的每个节点才是真实保存这组键值对的。)检查是否包含该元素时根据当前要存入的key在当前散列数组对应位置中的链表里是否已经包含这个key,若不包含则将这组键值对存入链表,否则就替换value。
    那么在获取元素时,HashMap同样先根据key的hashcode值进行散列算法,找到它在散列数组中的位置,然后遍历该位置的链表,找到该key所对应的value之后返回。
    看到这里可能有个疑问,链表中应该只能存入一个元素,那么HashMap是如何将key-value存入链表的某个节点的呢?实际上,HashMap会将每组键值对封装为一个Entry的实例,然后将该实例存入链表。
    1.2.2. hashcode方法
    HashMap的存取是依赖于key的hashcode方法的返回值的,而hashcode方法实际上是在Object中定义的。其定义如下:
    1.int hashCode()
    重写一个类的hashcode()方法有以下注意事项:
    1、若一个类重写了equals方法,那么就应当重写hashcode()方法。
    2、若两个对象的equals方法比较为true,那么它们应当具有相同的hashcode值。
    3、对于同一个对象而言,在内容没有发生改变的情况下,多次调用hashCode()方法应当总是返回相同的值。
    4、对于两个对象equals比较为false的,并不要求其hashcode值一定不同,但是应尽量保证不同,这样可以提高散列表性能。
    1.2.3. 装载因子及HashMap优化
    在散列表中有一下名词需要了解:
    ?Capacity:容量, hash表里bucket(桶)的数量, 也就是散列数组大小.
    ?Initial capacity:初始容量, 创建hash表的时 初始bucket的数量, 默认构建容量是16. 也可以使用特定容量.
    ?Size : 大小, 当前散列表中存储数据的数量.
    ?Load factor:加载因子, 默认值0.75(就是75%), 向散列表增加数据时如果 size/capacity 的值大于Load factor则发生扩容并且重新散列(rehash).
    那么当加载因子较小时候散列查找性能会提高, 同时也浪费了散列桶空间容量. 0.75是性能和空间相对平衡结果. 在创建散列表时候指定合理容量, 从而可以减少rehash提高性能。
    1.3. 有序Map
    1.3.1. LinkedHashMap实现有序的Map
    Map 接口的哈希表和链表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,LinkedHashMap维护着一个双向循环链表。此链表定义了迭代顺序,该迭代顺序通常就是将存放元素的顺序。
    需要注意的是,如果在Map中重新存入以有的key,那么key的位置会不会发生改变,只是将value值替换。
  12. 文件操作——File
    2.1. 创建File对象
    java.io.File用于表示文件(目录),也就是说程序员可以通过File类在程序中操作硬盘上的文件和目录。
    File类只用于表示文件(目录)的信息(名称、大小等),换句话说只能访问文件或目录的相关属性,不能对文件的内容进行访问。
    2.1.1. File(pathname)
    File提供了较多的构造方法来创建实例,其中之一就是:
    1.File(String pathname)
    通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例
    提示:抽象路径应尽量使用相对路径,并且目录的层级分隔符不要直接写”/”或””,应使用File.separator这个常量表示,以避免不同系统带来的差异。
    2.1.2. File(parent,child)
    File的另一个常用构造方法:
    1.File(File parent,String child)
    根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
    2.1.3. isFile() 方法
    File的isFile方法用于判断当前File对象表示的是否为一个文件
    1.boolean isFile()
    该方法若返回true,这表示File表示的是一个文件。
    2.2. File表示文件信息
    2.2.1. length方法
    File的length方法用于返回由此抽象路径名表示的文件的长度,其定义为:
    1.long length()
    该方法返回的long值表示该文件所占用的字节量。
    2.2.2. exists方法
    File的exists方法用于测试此抽象路径名表示的文件或目录是否存在,其方法定义:
    1.boolean exists()
    若该File表示的文件或目录存在则返回true,否则返回false。
    2.2.3. createNewFile方法
    File的createNewFile方法用于当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。 其方法定义:
    1.boolean createNewFile()
    返回值:如果指定的文件不存在并成功地创建,则返回 true;如果指定的文件已经存在,则返回 false 。
    2.2.4. delete方法
    File的delete方法用于删除此抽象路径名表示的文件或目录。 其方法定义:
    1.boolean delete()
    返回值:当且仅当成功删除文件或目录时,返回 true;否则返回 false。
    需要注意的是,若此File对象所表示的是一个目录时,在删除时需要保证此为空目录才可以成功删除(目录中不能含有任何子项)。
    2.2.5. isDirectory()
    File的isDirectory方法用于判断当前File对象表示的是否为一个目录
    1.boolean isDirectory()
    返回值:若File对象表示的是一个目录,则返回true
    2.3. File表示目录信息
    2.3.1. mkdir方法
    File的mkdir方法用于创建此抽象路径名指定的目录。其方法定义:
    1.boolean mkdir()
    返回值:当且仅当已创建目录时,返回 true;否则返回 false
    2.3.2. mkdirs方法
    File的mkdirs方法用于创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。注意,此操作失败时也可能已经成功地创建了一部分必需的父目录。其方法定义:
    1.boolean mkdirs()
    返回值:当且仅当已创建目录以及所有必需的父目录时,返回 true;否则返回 false
    2.3.3. delete方法
    2.2.5节中我们介绍了File的delete方法是用于删除此抽象路径名表示的文件或目录。在此强调,在删除目录时要特别注意:需要保证此为空目录才可以成功删除(目录中不能含有任何子项)。
    2.3.4. listFiles方法
    File的listFiles方法用于返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。其方法定义:
    1.File[] listFiles()
    返回值:抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件和目录。如果目录为空,那么数组也将为空。如果抽象路径名不表示一个目录,或者发生 I/O 错误,则返回 null。
    2.3.5. FileFilter接口
    通过listFiles方法我们可以获取一个目录下的所有子项,但有些时候我们并不希望获取全部子项,而是想获取部分满足我们实际需求的子项时,我们可以使用File的重载方法:
    1.File[] listFiles(FileFilter filter)
    这里我们看到,该重载方法 要求我们传入一个参数,其类型是FileFilter。什么是FileFilter呢? FileFilter是用于抽象路径名的过滤器,说白了就是定义一个规律规则,那么结合listFiles方法,我们就可以将满足此过滤规则的子项返回,其他则忽略。
    FileFilter是一个接口,所以当我们需要定义某种过滤规则时,我们可以定义一个类来实现这个接口,而此接口的实例可传递给 File 类的 listFiles(FileFilter) 方法。
    例如:
    1.File[] list = dir.listFiles(new FileFilter() {
    [email protected]
    3.????public boolean accept(File pathname) {
    4.????????return pathname.getName().startsWith(".");
    5.????}
    6.});
    该方法的参数FileFilter实例的accept方法并进行过滤,listFiles方法会将所有accept方法返回true的子项保留并返回。这个例子里我们会将dir中子项的名字以"."开头的返回。
评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2
迷上了代码!