浅谈排序算法

java集合框架

1.集合框架的由来

1.1什么是集合框架?

集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法.

1.2为什么要有集合框架?

其实在java2之前,java是没有完整的集合框架的,只有一些简答的容器类比如Vector类、Stack类,HashTable类等等。容器类就是用来存储数据。这里数据只能是引用类型的数据,当然,一说到存储数据很快我们会想到数组,数组是可以存储相同类型,可以是基本类型,也可以是引用类型。

数组也存在很多的弊端:

  • 数组一旦初始化,长度是固定的,不能再改变了。
  • 每次使用都得编写操作数组的方法,体现不了java的封装思想。

java把集合框架的类和接口都放在了java.util包中

2.集合体系

3.List接口

Collection是存储数据的容器。

常用操作方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

增加:
boolean add(Object e) 将指定元素添加到此向量的末尾,等价于addElement方法。
void add(int index, Object element) 在此向量的指定位置插入指定的元素。
boolean addAll(Collection c) :把c集合中的元素添加到当前集合对象中.

删除:
Object remove(int index) :删除指定索引位置的元素,并返回删除之后的元素.
boolean remove(Object o):删除指定的元素.
boolean removeAll(Collection c):从此集合中移除包含在指定 集合c中的所有元素。
boolean retainAll(Collection c):在此集合中仅保留包含在指定 集合c中的元素,求两个集合的交集。

修改:
Object set(int index, Object element) :修改当前集合中指定索引位置的元素.
返回被替换的旧的元素.

查询:
int size() :返回当前集合中存储几个元素.
boolean isEmpty():判断当前集合中元素个数是否为0.
Object get(int index):查询指定索引位置的元素.
Object[] toArray():把集合对象转换为Object数组.

3.1Vector类

集合框架出现之前,有一个容器类就是Vector类,Vector类的底层其实就是一个Object数组 protected Object [] elementData;

Vector类现在已经基本上被ArrayList取代了

3.1.1Vector类的存储原理

  • 底层是一个Object类型的数组,所以可以存储任意类型的对象,注意:集合中只能存储对象,不能存储基本类型的数据。但是java5之后支持了自动装箱操作,可以基本类型的值自动装箱为包装类型,但也存储的是对象.
  • 集合中存储的对象,都是存储的是对象的引用,并不是对象的本身.如下:
1
2
3
4
5
6
Vector v2 = new Vector();
StringBuilder sb = new StringBuilder("SSSS");
v2.add(sb);//集合类中存储的对象,存储的是对象的引用,并不是存储的值
System.out.println(v2);//SSSS
sb.append(222);
System.out.println(v2);//SSSS222

3.2Stack类

Stack是数据结构的一种,先进先出原则,Stack类继承于Vector类,底层可以用数组存储,也可以用链表来存储,官方建议使用ArrayQueue

1
2
3
Deque 接口及其实现提供了 LIFO 堆栈操作的更完整和更一致的 set,应该优先使用此 set,而非此类。例如: 

Deque<Integer> stack = new ArrayDeque<Integer>();

3.3ArrayList类

ArrayList类是Java集合框架出现之后用来取代Vector类的。二者底层原理都是基于数组的算法,一模一样.

区别

  • Vector: 所有的方法都使用了synchronized修饰符. 线程安全但是性能较低. 适用于多线程环境.

  • ArrayList:所有的方法都没有使用synchronized修饰符. 线程不安全但是性能较高.

  • 即使以后在多线程环境下,我们也不使用Vector类: ArrayList list = Collections.synchronizedList(new ArrayList(…));

在Java7之前,即使使用new ArrayList创建对象,一个元素都不存储,但是在堆空间依然初始化了长度位10的Object数组,没必要. 从Java7开始优化这个设计,new ArrayList,其实底层创建的使用一个空数组. Object [] elementData = new Object[]{}; 在第一次调用add方法的时候,才会重新去初始化数组.

3.4 LinkedList类

LinkedList类是双向链表,单向链表双向队列和栈 的实现类

优势:无论是链表还是队列,它们都擅长操作头和尾,所以在LinkedList中的大多数方法都是xxFirst()

看API会发现,LinkedList中依然存在get(int index)方法,也就是根据索引来确定存储的值,链表中是没有索引的概念的,之所以有这个方法是因为有了集合框架,让LinkedList作为了List接口的实现,没法发只能来实现这个方法,我们也该少用这个方法

3.5List接口实现类比较

面向接口编程:

1
接口类型  变量 =   new  实现类();List      list = new ArrayList();

三者共同的特点(共同遵循的规范):

  • 1):允许元素重复.
  • 2):记录元素的先后添加顺序.
    • Vector类: 底层采用数组结构算法,方法都使用了synchronized修饰,线程安全,但是性能相对于ArrayList较低.
    • ArrayList类: 底层采用数组结构算法,方法没有使用synchronized修饰,线程不安全,性能相对于Vector较高.ArrayList现在机会已经取代了Vector,为了保证ArrayList的线程安全,List list = Collections.synchronizedList(new ArrayList(…));

4.Set接口

Set是Collection子接口,模拟了数学上的集的概念。

特点:

  • 不允许元素重复
  • 不会记录元素的添加顺序

Set判断两个对象是否相等用equals,而不是使用==。也就是说两个对象equals比较返回true,Set集合是不会接受这个两个对象的。

4.1HashSet类

HashSet是Set接口最常用的一个类,底层采用的是哈希表算法

在HashSet中如何判断两个对象相同?

当往集合中添加新的对象,先判断该对象和集合对象中的HashCode值:

  • 不等,直接把该对象存储到HashCode 指定的位置
  • 相等,在据需判断新的对象和集合中的equals比较

二者缺一不可

每一个存储到hash表中的对象,都得提供hashCode和equals方法,用来判断是否是同一个对象.存储在哈希表中的对象,都应该覆盖equals方法和hashCode方法,并且保证equals相等的时候,hashCode也应该相等.

4.2LinkedHashSet类

顾名思义,底层用的是链表和哈希表算法,链表记录元素的添加顺序,哈希表保证元素不重复。。综合了List接口和Set接口的特点,很强势,但是性能并不高

LinkedHashSet类是HashSet类的子类

4.3TreeSet类

底层采用的是红黑树算法,会对存储的元素默认进行自然排序,从小到大。

  • 自然排序

TreeSet调用集合元素的compareTo方法来比较元素的大小关系,然后讲集合元素按照升序排列(从小到大).注意:要求TreeSet集合中元素得实现java.util.Comparable接口.覆盖 public int compareTo(Object o)方法,在该方法中编写比较规则.在该方法中,比较当前对象(this)和参数对象o做比较(严格上说比较的是对象中的数据). this > o: 返回正整数. 1 this < o: 返回负整数. -1 this == o: 返回0. 此时认为两个对象为同一个对象.

  • 定制排序

在创建对象的时候,在TreeSet构造器中传递java.lang.Comparator对象.并覆盖public int compare(Object o1, Object o2)再编写比较规则.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package set;
import java.util.*;
/**
* @author 15626
* TreeSet 底层采用的是是红黑树算法,会对存储的 元素进行自然排序(从小到大)
* 要排序当然要保证类型相同,可以使用泛型进行规范
* 排序规则:数字就进行比大小,字符就按照Unicode编码规范来比较(前128位与ASCII编码重复)
*/
public class TreeSetDemo {

public static void main(String []args){
Set<Student> set = new TreeSet<Student>();
set.add(new Student("张三",30));
set.add(new Student("李四",20));
set.add(new Student("王麻子",10));
System.out.println(set);


/**
* 定制排序:在TreeSet构造器中传一个实现Comparator接口类的一个对象,
* 在实现类中覆盖Comparator接口中的public int compare(Ojbect o1,Object o2)方法
* 在方法中可以编写比较规则
*/

//定制排序:根据人名长度进行排序
Set<Student> set2 = new TreeSet<Student>(new NameLengthComparator());
set2.add(new Student("张三李四",30));
set2.add(new Student("李四",20));
set2.add(new Student("王麻子",10));
System.out.println(set2);
}
}
class NameLengthComparator implements Comparator<Student>{

@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
if(o1.name.length()>o2.name.length()){
return 1;
}else if(o1.name.length() < o2.name.length()){
return -1;
}else{
return 0; //说明是同一个对象
}
}

}
class Student implements Comparable<Student>{
String name;
int age;

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

@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}

/* 自然排序:覆盖Comparable接口中的compareTo方法
* this > o ,返回正整数
* this < o ,返回负整数
* this = o ,返回0 //说明两个对象相同
*
*/

@Override
//自然排序:根据年龄排序

public int compareTo(Student o) {
if(this.age > o.age){
return 1;
}else if(this.age < o.age){
return -1;
}
return 0;
}
}

4.4 Set实现类的比较

共同点:

  • 都不允许元素重复
  • 都不是线程安全的类

不同:

  • HashSet 不保证元素的先后添加顺序.底层采用的是哈希表算法,查询效率极高.
    • 判断两个对象是否相等的规则:
      • equals比较为true
      • hashCode值相同.
      • 要求存在在哈希中的对象元素都得覆盖equals和hashCode方法.
  • LinkedHashSet HashSet的子类,底层也采用的是哈希表算法,但是也使用了链表算法来维持元素的先后添加顺序.
    • 判断两个对象是否相等的规则和HashSet相同. 因为需要多使用一个链表俩记录元素的顺序,所以性能相对于HashSet较低.
  • TreeSet 不保证元素的先后添加顺序,但是会对集合中的元素做排序操作,底层采用红黑树算法
    • 自然排序: 要求在TreeSet集合中的对象必须实现java.lang.Comparable接口,并覆盖compareTo方法.
    • 定制排序: 要求在构建TreeSet对象的时候,传入一个比较器对象(必须实现java.lang.Comparator接口). 在比较器中覆盖compare方法,并编写比较规则.
    • TreeSet判断元素对象重复的规则: compareTo/compare方法是否返回0.如果返回0,则视为是同一个对象.

5.Map接口

严格上说,Map并不是集合,而是两个集合之间的映射关系(Map接口并没有继承于Collection接口),然而因为Map可以存储数据(每次存储都应该存储A集合中以一个元素(key),B集合中一个元素(value)),我们还是习惯把Map也称之为集合.

Map接口并没有继承于Collection接口也没有继承于Iterable接口,所以不能直接对Map使用for-each操作.

  • 可以把Map中的key看做一个Set集合(不允许重复)
  • 可以吧Map中的value看做一个List集合(允许重复)
  • 可以把Entry(key-value)键值对看做一个Set集合(不允许重复)

如此以来,可以间接的对Map使用for-each

其实,相同算法的Set底层用的是相同算法的Map. 把Set的集合对象作为Map的key,再使用一个Object常量做为value.

5.1Map的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void clear() 
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
int hashCode()
返回此映射的哈希码值。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true
Set<K> keySet()
返回此映射中包含的键的 Set 视图。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()
返回此映射中的键-值映射关系数。
Collection<V> values()
返回此映射中包含的值的 Collection 视图。

5.2HashMap类

采用哈希表算法, 此时Map中的key不会保证添加的先后顺序,key也不允许重复.

key判断重复的标准是: key1和key2是否equals为true,并且hashCode相等.

5.3TreeMap类

采用红黑树算法,此时Map中的key会按照自然顺序或定制排序进行排序,,key也不允许重复. key判断重复的标准是: compareTo/compare的返回值是否为0.

5.4 LinkedHashMap类

采用链表和哈希表算法,此时Map中的key会保证先后添加的顺序,key不允许重复.

key判断重复的标准和HashMap中的key的标准相同.

5.5 Hashtable类

采用哈希表算法,是HashMap的前身(类似于Vector是ArrayList的前身)

在Java的集合框架之前,表示映射关系就使用Hashtable.

HashMap和TreeMap以及LinkedHashMap都是线程不安全的,但是性能较高。

解决方案: Map m = Collections.synchronizedMap(Map对象);

6.集合工具类

6.1 Arrays类

在Collection接口中有一个方法叫toArray把集合转换为Object数组. 把集合转换为数组: Object[] arr = 集合对象.toArray();

数组也可以转换为集合(List集合): public static List asList(T… a) 等价于public static List asList(T[] a).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package util;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

/**
* @author 15626
* 集合的工具类 :Arrays,可以把数组转换成集合
*/
public class ArraysDemo {
public static void main(String []args){
//把集合转换成数组
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("d");
Object [] arr= list.toArray();
for(Object arr1 : arr){
System.out.println(arr1);
}

System.out.println("========================");

//把数组转化成集合
String [] str = new String[]{"A","b","C"};

//返回的是不能改变长度的list对象,因为返回的ArrayList不是java.util.ArrayList包中的对象,而是Arrays类中的内部类对象
List<String> list2 = Arrays.asList(str);
//list2.remove(0);//UnsupportedOperationExceptions
System.out.println(list2);

//基本类型的数据会自动装箱转化为包装类型,这里的1,2,3都自动装箱为Integer类型
List<Integer> list3 = Arrays.asList(1,2,3,4,5);
System.out.println(list3);

//试图把数组直接转化一下,这是直接把数组当做对象,基本类型的数据不能存储到集合中
int [] a = new int[]{1,2,3,4,5};
List<int[]> list4 = Arrays.asList(a);
System.out.println(list4);
}
}

6.2Collections类

HashSet/ArrayList/HashMap都是线程不安全的,在多线程环境下不安全. 在Collections类中有获取线程安全的集合方法:

1
2
3
Set  set = Collections.synchronizedSet(new HashSet());
Map map = Collections.synchronizedMap(new HashMap());
List list = Collections.synchronizedList(new ArrayList());

7.泛型

集合框架牵扯到了泛型,这有篇文章对泛型有很详细的解释:https://www.cnblogs.com/coprince/p/8603492.html

8.测试代码

https://github.com/EarthSoar/JavaExamples/tree/master/CollectionsFramework

文章目录
  1. 1. java集合框架
    1. 1.1. 1.集合框架的由来
      1. 1.1.1. 1.1什么是集合框架?
      2. 1.1.2. 1.2为什么要有集合框架?
    2. 1.2. 2.集合体系
    3. 1.3. 3.List接口
      1. 1.3.1. 3.1Vector类
        1. 1.3.1.1. 3.1.1Vector类的存储原理
      2. 1.3.2. 3.2Stack类
      3. 1.3.3. 3.3ArrayList类
      4. 1.3.4. 3.4 LinkedList类
      5. 1.3.5. 3.5List接口实现类比较
    4. 1.4. 4.Set接口
      1. 1.4.1. 4.1HashSet类
      2. 1.4.2. 4.2LinkedHashSet类
      3. 1.4.3. 4.3TreeSet类
      4. 1.4.4. 4.4 Set实现类的比较
    5. 1.5. 5.Map接口
      1. 1.5.1. 5.1Map的常用方法
      2. 1.5.2. 5.2HashMap类
      3. 1.5.3. 5.3TreeMap类
      4. 1.5.4. 5.4 LinkedHashMap类
      5. 1.5.5. 5.5 Hashtable类
    6. 1.6. 6.集合工具类
      1. 1.6.1. 6.1 Arrays类
      2. 1.6.2. 6.2Collections类
    7. 1.7. 7.泛型
    8. 1.8. 8.测试代码