Java基础--集合

来源:互联网 发布:怡橙假期 知乎 编辑:程序博客网 时间:2024/06/06 03:22

集合框架、集合操作—线性表

1集合框架
2集合操作——线性表


1. 集合框架

1.1. Collection

java提供了一种可以存数一组数据的数据结构,其提供了丰富的方法,在实际开发中往往比数组使用的广泛。这种数据结构成为集合:Collection。
Collection是一个接口,其定义了集合的相关功能方法。

1.1.1. List和Set

Collection派生出了两个子接口,一个是List另一个则是Set。
List:称为可重复集,顾名思义,该集合中是允许存放重复元素的,那么何为重复元素?重复元素指的并非是同一个元素,而是指equals方法比较为true的元素。
Set:称为不可重复集,所以,该集合中是不能将相同的元素存入集合两次,同List,这里相同指的也是两个元素的equals比较结果为true。

1.1.2. 集合持有对象的引用

集合中存储的都是引用类型的元素,那么引用类型变量实际上存储的是对象的“地址”,所以实际上集合只存储了元素对象在堆中的地址。而并不是将对象本身存入了集合中。

1.1.3. add()方法

Collection定义了一个add方法用于向集合中添加新元素。 该方法定义为:

boolean add(E e)
该方法会将给定的元素添加进集合,若添加成功则返回true,否则返回false
例如:

Collection< span>String< c =new ArrayList< span>String<();System.out.println(c);// []c.add("a");c.add("b");c.add("c");System.out.println(c);// [a, b, c]

这里Collection后面的 是泛型,我们后面会讲解,这里不写也没有关系。

1.1.4. contains方法

boolean contains(Object o)
该方法会用于判断给定的元素是否被包含在集合中。若包含则返回true,否则返回false。
这里需要注意的是,集合在判断元素是否被包含在集合中是使用元素的equals的比较结果。
(o==null ? e==null : o.equals(e)) 其中e是集合中的元素。
例如:

Collection< span>Cell< cells =new ArrayList< span>Cell<();cells.add(newCell(1,2));cells.add(newCell(1,3));cells.add(newCell(2,2));cells.add(newCell(2,3));Cell cell =newCell(1,3);// List集合contains方法和对象的equals方法相关boolean flag = cells.contains(cell);// 如果Cell不重写equals方法将为falseSystem.out.println(flag);// true 

1.1.5. size,clear,isEmpty方法

size方法用于获取当前集合中的元素总数。该方法定义为:int size()
clear方法用于清空集合。该方法定义为:void clear()
isEmpty方法用于判断当前集合中是否不包含元素。该方法定义为:boolean isEmpty()
例如:

Collection< span>String< c =new HashSet< span>String<();System.out.println(c.isEmpty());// true c.add("java");c.add("cpp");c.add("php");c.add("c#");c.add("objective-c");// isEmpty:false, size: 5System.out.println("isEmpty:"+ c.isEmpty()+",size: "+ c.size());c.clear();// isEmpty:true, size: 0System.out.println("isEmpty:"+ c.isEmpty()+", size: "+ c.size());

1.1.6. addAll与containsAll方法

集合也提供了批处理操作:
addAll方法用于将给定集合中的所有元素添加到当前集合中,其方法定义为:

boolean addAll(Collection c)
例如:

Collection< span>String< c1 =new ArrayList< span>String<();c1.add("java");c1.add("cpp");c1.add("php");c1.add("c#");c1.add("objective-c");System.out.println(c1);// [java, cpp, php, c#, objective-c] Collection< span>String< c2 =new HashSet< span>String<();c2.addAll(c1);System.out.println(c2);// [cpp, php, c#, java, objective-c] 

containsAll方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回true。其方法定义为:

boolean containsAll(Collection c)
例如:

Collection< span>String< c1 =new ArrayList< span>String<();c1.add("java");c1.add("cpp");c1.add("php");c1.add("c#");c1.add("objective-c");System.out.println(c1);// [java, cpp, php, c#, objective-c] Collection< span>String< c2 =new ArrayList< span>String<();c2.add("java");c3.add("cpp");System.out.println(c1.containsAll(c2));// true 

1.2. Iterator 迭代器

Collection提供了一个遍历集合的通用方式,迭代器(Iterator)。
获取迭代器的方式是使用Collection定义的方法:

Iterator iterator()
迭代器Iterator是一个接口,集合在覆盖Collection的iterator()方法时提供了迭代器的实现。
Iterator提供了统一的遍历集合元素的方式。

1.2.1. hasNext与next方法

迭代器用于遍历集合的两个主要方法:
boolean hasNext():判断集合是否还有元素可以遍历。
E next():返回迭代的下一个元素

  • 遍历集合应遵循“先问后取”的方式,也就是说,应当在确定hasNext()方法的返回值为true的情况下再通过next()方法取元素。

由此可以看出,使用迭代器遍历集合是通过boolean值驱动的,所以它更适合使用while循环来遍历。
例如:

Collection< span>String< c =new HashSet< span>String<();c.add("java");c.add("cpp");c.add("php");c.add("c#");c.add("objective-c");Iterator< span>String< it = c.iterator();while(it.hasNext()){String str = it.next();    System.out.println(str);}

1.2.2. remove方法

迭代器还提供了一个方法:void remove()。该方法用于删除迭代器当次从集合中获取的元素。若我们在迭代过程中想删除集合元素时,我们就需要通过该方法来进行。这里需要注意,在使用迭代器遍历集合时是不能通过集合自身提供的remove方法删除元素的,否则迭代器在迭代时会抛出异常。
例如:

Collection< span>String< c =new HashSet< span>String<();c.add("java");c.add("cpp");c.add("php");c.add("c#");c.add("objective-c");System.out.println(c);// [cpp, php, c#, java, objective-c] Iterator< span>String< it = c.iterator();while(it.hasNext()){String str = it.next();if(str.indexOf("c")!=-1){        it.remove();}}System.out.println(c);// [php, java]    

1.2.3. 增强for循环

Java5.0之后推出了一个新的特性,增强for循环,也称为新循环。该循环不通用于传统循环的工作,其只用于便利集合或数组。
语法:

for(元素类型 e : 集合或数组){循环体}

新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式。所以新循环本质上是迭代器。
例如:

Collection< span>String< c =new HashSet< span>String<();c.add("java");c.add("cpp");c.add("php");c.add("c#");c.add("objective-c");for(String str : c){    System.out.print(str.toUpperCase()+" ");}// CPP PHP C# JAVA OBJECTIVE-C

1.3. 泛型机制

1.3.1. 泛型在集合汇总的应用

泛型是Java SE 5.0引入的特性,泛型的本质是参数化类型。在类、接口和方法的定义过程中,所操作的数据类型被传入的参数指定。
Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中的对象类型。Java编译器可以据此进行类型检查,这样可以减少代码在运行时出现错误的可能性。
我们来举个例子,比如ArrayList,其在定义时是这样的:

publicclass ArrayList< span>E<{    … … …                public boolean add(E e){…};public E get(int index){…};}

由此我们可以看出,再声明ArrayList时,类名的右侧有一个 。”<>”表示泛型,而其中可以使用数字字母下划线(数字不能的第一个字符)来表示泛型的名字。(通常我们使用一个大写字母来表示,当然这个不是规定。)这时,在类中声明的方法的参数,返回值类型可以被定义为泛型。这样在创建对象时可以将类型作为参数传递,此时,类定义所有的E将被替换成传入的参数。
例如:

ArrayList< span>String< list =new ArrayList< span>String<();//泛型E在这里被指定为String类型list.add("One");//那么add方法的参数就被替换为String类型list.add(100);//这里就会出现编译错误,因为这里的参数应为String类型。

2. 集合操作——线性表

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):将给定的元素存入给定位置,并将原位置的元素返回。
例如:

List< span>String< list =new ArrayList< span>String<();list.add("java");list.add("cpp");list.add("php");list.add("c#");list.add("objective-c");// get方法遍历Listfor(int i =0; i < span> list.size(); i++){    System.out.println(list.get(i));}String value = list.set(1,"c++");System.out.println(value);// cppSystem.out.println(list);// [java, c++, php, c#, objective-c] // 交换位置1和3上的元素list.set(1, list.set(3, list.get(1)));System.out.println(list);// [java, c#, php, c++, objective-c]

2.1.3. 插入和删除

List根据下标的操作还支持插入与删除操作:
void add(int index,E element):
将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。
E remove(int index):
删除给定位置的元素,并将被删除的元素返回。
例如:

List< span>String< list =new ArrayList< span>String<();list.add("java");list.add("c#");System.out.println(list);// [java, c#]list.add(1,"cpp");System.out.println(list);// [java, cpp, c#]list.remove(2);System.out.println(list);// [java, cpp]

2.1.4. subList方法

List的subList方法用于获取子List。
需要注意的是,subList获取的List与原List占有相同的存储空间,对子List的操作会影响的原List。
List subList(int fromIndex, int toIndex);
fromIndex和toIndex是截取子List的首尾下标(前包括,后不包括) 。
例如:

List< span>Integer< list =new ArrayList< span>Integer<();for(int i =0; i < span>10; i++){    list.add(i);}System.out.println(list);// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]List< span>Integer< subList = list.subList(3,8);System.out.println(subList);// [3, 4, 5, 6, 7] // subList获得的List和源List占有相同的数据空间for(int i =0; i < span> subList.size(); i++){    subList.set(i, subList.get(i)*10);}System.out.println(subList);// [30, 40, 50, 60, 70]System.out.println(list);// [0, 1, 2, 30, 40, 50, 60, 70, 8, 9] // 可以用于删除连续元素list.subList(3, 8).clear();System.out.println(list);

2.1.5. List转换为数组

List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
其有两个方法:

1.Object[] toArray()
2.T[] toArray(T[] a)
其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。
例如:

List< span>String< list =new ArrayList< span>String<();list.add("a");list.add("b");list.add("c");//通常我们传入的数组不需要给定长度String[] strArr = list.toArray(newString[]{});        System.out.println(Arrays.toString(strArr));// [a, b, c]

2.1.6. 数组转换为List

Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。
其方法定义为:
static List asList
返回的List的集合元素类型由传入的数组的元素类型决定。
需要注意的是,返回的集合我们不能对其增删元素,否则会抛出异常。并且对集合的元素进行的修改会影响数组对应的元素。
例如:

String[] strArr ={"a","b","c"};List< span>String< list = Arrays.asList(strArr);System.out.println(list);// [a, b, c]// list.add("d"); // 会抛出UnsupportedOperationException //     java.util.Arrays$ArrayList System.out.println(list.getClass().getName());List< span>String< list1 =new ArrayList< span>String<();list1.addAll(Arrays.asList(strArr));

2.2. List排序

2.2.1. Collections.sort方法实现排序

Collection是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。该方法的定义为:
void sort(List list)
其作用是对集合元素进行自然排序(按照元素的由小至大的顺序)
例如:

List< span>Integer< list =new ArrayList< span>Integer<();Random r =newRandom(1);for(int i =0; i < span>10; i++){    list.add(r.nextInt(100));}System.out.println(list);// [85, 88, 47, 13, 54, 4, 34, 6, 78, 48]Collections.sort(list);System.out.println(list);// [4, 6, 13, 34, 47, 48, 54, 78, 85, 88]

2.2.2. Comparable

通过上面我们知道了如何对集合元素进行自然排序,但是要想对元素进行自然排序那么就必须要有一个必要条件,就是元素的大小。集合中存入的都是引用类型,是以对象的形式存在于内存中,那么对象是如何进行的大小比较呢?实际上,若想对某个集合的元素进行自然排序,该集合的元素有一个要求,就是这些元素必须是Comparable的子类。
Comparable是一个接口,用于定义其子类是可以比较的。因为该接口有一个抽象方法:
int compareTo(T t)
所有子类都需要重写该方法来定义对象间的比较规则。该方法要求返回一个整数,这个整数不关心具体的值,而是关注取值范围。
当返回值<0时,表示当前对象比参数给定的对象大。
当返回值<0 p>
当返回值=0时,表示当前对象和参数给定的对象相等。
例如:

Class Cell implements Comparable< span>Cell<{    int row;    int col;publicCell(int row,int col){this.row = row;this.col = col;}public int compareTo(Cell c){//根据row比较大小returnthis.row - c.row;}}

那么Collections的sort在进行排序时就会根据集合中元素的compareTo方法的返回值来判断大小从而进行自然排序。

// Cell实现了Comparable接口,CompareTo方法逻辑为按照row值的大小排序List< span>Cell< cells =new ArrayList< span>Cell<();cells.add(newCell(2,3));cells.add(newCell(5,1));cells.add(newCell(3,2));Collections.sort(cells);System.out.println(cells);// [(2,3), (3,2), (5,1)]

2.2.3. comparator

一旦Java类实现了Comparable,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。
该接口要求实现类必须重写其定义的方法:
int compare(T o1,T o2)
该方法的返回值要求,若o1

List< span>Cell< cells =new ArrayList< span>Cell<();cells.add(newCell(2,3));cells.add(newCell(5,1));cells.add(newCell(3,2));// 按照col值的大小排序Collections.sort(cells,new Comparator< span>Cell<(){    @Overridepublic int compare(Cell o1, Cell o2){return o1.col - o2.col;}});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():返回队首元素,但是不删除。
例如:

Queue< span>String< queue =new LinkedList< span>String<();queue.offer("a");queue.offer("b");queue.offer("c");System.out.println(queue);// [a, b, c] String str = queue.peek();System.out.println(str);// a while(queue.size()<0){    str = queue.poll();    System.out.print(str +" ");// a b c}

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():将栈首元素删除并返回。
例如:

Deque< span>String< stack =new LinkedList< span>String<();stack.push("a");stack.push("b");stack.push("c");System.out.println(stack);// [c, b, a] String str = stack.peek();System.out.println(str);// c while(stack.size()<0){    str = stack.pop();    System.out.print(str +" ");// c b a}

本文到此完结。

1 1