集合操作------线性表
1、 List
2、 List的排序
3、 队列和栈
List:
1、 get和set
List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下表的get和set方法:
--E get(int index):获取集合中指定下标对应的元素,下标从0开始。
--E set(intindex,E element):将给定元素存入给定位置,并将原位置的元素返回。
2、插入和删除
List根据下标的操作还支持插入与删除操作。
--add(int index,E element):将给定的元素插入到指定的位置,原位置及后续元素都顺序向后移动。
--remove(int index):删除给定位置的元素,并将被删除的元素返回。
3、ArrayList(变长数组)和LinkedList(双向循环列表)
List接口是Collection的子接口,用于定义线性表数据结构。可以将List理解为存放对象的数组,只不过其元素个数可以动态的增加或减少。
List接口的两个常见实现类为ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。
可以认为ArrayList和LinkedList的方法再逻辑上完全一样,只是在性能上有一定的差别。ArrayList更适合随机访问,而LinkedList更适合在头部和尾部插入和删除。在性能要求不是特别苛刻的情况下可以忽略这个差别。
public void testList1() {
Random rand=new Random();
ArrayList<Integer> list1=new ArrayList<Integer>();
LinkedList<Integer> list2=new LinkedList<Integer>();
for(int i=0;i<10000;i++) {
int j=rand.nextInt();
list1.add(j);
list2.add(j);
}
//反复在集合的头部(0号位置)添加删除元素
long t1=System.nanoTime();//纳秒
for(int i=0;i<10000;i++) {
list1.add(0,5);//将5插入到0号位置,后面元素将顺序向后移动,是很消耗性能的。
list1.remove(0);//将0号元素删掉,后面元素将顺序向前移动,是很消耗性能的。
}
long t2=System.nanoTime();
for(int i=0;i<10000;i++) {
list2.add(0,5);//插入,按位置插入
list2.remove(0);//删除,按位置删除
}
long t3=System.nanoTime();
System.out.println("顺序表消耗的时间:"+(t2-t1));//顺序表消耗的时间:26815138
System.out.println("双向链表消耗的时间:"+(t3-t2));//双向链表消耗的时间:3252906
//结论:LinkedList头尾添加和删除性能好于ArrayList
t1=System.nanoTime();
for(int i=0;i<10000;i++) {
int n=list1.get(5000);//获取指定index位置的元素
}
t2=System.nanoTime();
for(int i=0;i<10000;i++) {
int n=list2.get(5000);
}
t3=System.nanoTime();
System.out.println("顺序表消耗的时间:"+(t2-t1));//顺序表消耗的时间:596195
System.out.println("双向链表消耗的时间:"+(t3-t2));//双向链表消耗的时间:102743580
//结论:ArrayList(中部)的读取性能好于LinkedList
//当不知道该选用哪个List的时候,就选用ArrayList。
}
public void testList() {
List<String> list=new ArrayList<String>();
list.add("Tom");//在尾部添加
list.add("Jerry");//在尾部添加
System.out.println(list);//[Tom, Jerry]
// 0 1
String name=list.get(1);//返回1号位置的元素
System.out.println(name);//Jerry
String one=list.set(1, "Nemo");//set 将1号位置的Jerry换成Nemo,并且将Jerry返回
System.out.println(one);//Jerry
System.out.println(list);//[Tom, Nemo]
list.add(1, "Jerry");//在1的位置添加Jerry,其余的元素顺序后移
System.out.println(list);//[Tom, Jerry, Nemo]
list.remove(0);//将0号位置的元素删除,其余元素顺序向前移动
System.out.println(list);//[Jerry, Nemo]
//以上方法都是与index(位置)有关的操作方法,是List扩展的方法,此外List还从Collection继承了全部的集合操作方法
}
4、 subList:
List的subList方法用于获取子List。需要注意的是,subList获取的List与原List占有相同的存储空间,对subList的操作会影响原List。
List<E>subList(int fromIndex,int toIndex);fromIndex和toIndex是截取List的首尾下标,包括fromIndex,不包括toIndex。
public void testSublist() {
List<String> cards=new ArrayList<String>();
cards.add("黑桃2");//0
cards.add("红桃2");//1
cards.add("红桃10");//2
cards.add("梅花10");//3
cards.add("方片10");//4
cards.add("黑桃10");//5
cards.add("黑桃3");//6
List<String> bomb=cards.subList(2, 6);//炸弹是从2到6,包括2,不包括6
System.out.println(bomb);//[红桃10, 梅花10, 方片10, 黑桃10]
bomb.clear();
System.out.println(cards);//[黑桃2, 红桃2, 黑桃3]
//list和sublist共享一块内存空间
}
总结List:
List继承了Collection,扩展了与位置有关的操作方法,实现类有两个:ArrayList和LinkedList,两个实现类的方法一致,都是List。任何时候,需要使用集合,但是不能确定使用哪个实现类的时候,首先选择ArrayList。
5、 List转换为数组
List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
其有两个方法:
Object[] toArray;这个只能转换成Object类型的数组
public void testToArry1() {
//Java先编译后运行,编译阶段没有对象,只有变量的类型,运行期间才有对象,对象可能是多态的。
Collection<String> list=new ArrayList<String>();
list.add("Tom");
list.add("Andy");
list.add("Nemo");
list.add("Jerry");
Object[] names=list.toArray();
//names的每个元素变量类型是Object类型,元素变量类型不是String类型!不是String[]!
//[Object[0],Object[1],...]
System.out.println(Arrays.toString(names));//[Tom, Andy, Nemo, Jerry]
//返回“运行期间”对象的类型!
System.out.println(names[0].getClass().getName());//java.lang.String
}
<T>T[] toArray(T[] a);可以转换成指定类型的数组,如果这个数组的长度小于数组元素的个数,则返回新数组。如果数组的长度大于数组元素的个数,返回原数组,没有值的数组位置存放null。
public void testToArray2() {
Collection<String> list=new ArrayList<String>();
list.add("Tom");
list.add("Andy");
list.add("Nemo");
list.add("Jerry");
String[] name1=new String[2];//新建一个String类型的数组,长度为2,但是添加的元素个数是4,数组长度不够,则返回新数组
String[] name2=new String[6];//新建一个String类型的数组,长度为6,但是添加的元素个数是4,没有值的填充0,返回原数组
String[] name3=new String[list.size()];//最理想的情况下
//返回的是String[]类型的数组
name1=list.toArray(name1);//返回新数组
name2=list.toArray(name2);//返回原数组
name3=list.toArray(name3);//返回原数组
System.out.println(Arrays.toString(name1));//[Tom, Andy, Nemo, Jerry]
System.out.println(Arrays.toString(name2));//[Tom, Andy, Nemo, Jerry, null, null]
System.out.println(Arrays.toString(name3));//[Tom, Andy, Nemo, Jerry]
}
6、 数组转换为List
Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。返回的List的集合元素类型由传入的数组的元素类型决定。并且需要注意的是,返回的集合我们不能对其进行增删元素,否则会抛出异常。并且对集合的元素进行修改会影响数组对应的元素。
public void testAsList() {
//数组转换为List
String[] names= {"Tom","Andy","Jerry"};
//list是静态的list,长度不能改变,也就是不能调用add和remove
List<String> list1=Arrays.asList(names);
//静态list和name共享同一个内存空间,改数组,list也会被更改
//不是ArrayList也不是LinkedList,这个list的类型是内部类
System.out.println(list1.getClass().getName());//java.util.Arrays$ArrayList是一个内部类
System.out.println(list1);//[Tom, Andy, Jerry]
//list1.remove(0);//会出现运行异常,不支持的操作
//list1.add();
//数组转换为List的第二种方法
List<String> list2=new ArrayList<String>();
//list2和name不共享内存空间,更改数组,对list没有影响
Collections.addAll(list2, names);//将names中所有元素添加到list2中
System.out.println(list2.getClass().getName());//java.util.ArrayList
System.out.println(list2);
//静态List与数组names共享相同的存储空间
//修改数组names影响线性表List
names[1]="Lee";
System.out.println(list1);//[Tom, Lee, Jerry]
System.out.println(list2);//[Tom, Andy, Jerry]
}
List的排序:
排序:按照一定的大小顺序排序
比较大小:比较两个元素的大小
public void testStringCompare() {
//String类型如何比较大小
//在String类中有一个方法compareTo
String s1;
String s2;
s1="A";
s2="B";
if(s1.compareTo(s2)>0) {
System.out.println("s1大于s2");
}
if(s1.compareTo(s2)<0) {
System.out.println("s1小于s2");
}
if(s1.compareTo(s2)==0) {
System.out.println("s1等于s2");
}
//String是可以比较大小的,包含默认
//比较大小的方法compareTo
//Java中凡是可以默认比较大小的类型,都有compareTo,比如:包装类Date
//有compareTo方法的类,称为可以自然的类
//compareTo是compareable接口定义的方法,可以自然排序的类都实现了这个接口
//凡是实现compareable,实现了compareTo方法的类,都可以进行自然(默认)排序。
}
1、 Collections.sort方法实现排序
Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。
该方法的定义为:
--voidsort(List<T> list):该方法的作用是对给定的集合元素进行自然排序。
List的每个元素要有默认比较规则,sort根据元素的默认比较规则进行排序,若没有默认的比较规则,则不能排序。
public void testSort1() {
List<Integer> list=new ArrayList<Integer>();
Random ran=new Random();
for(int i=0;i<10;i++) {
list.add(ran.nextInt(100));
}
System.out.println(list);//[16, 44, 85, 60, 19, 98, 31, 57, 78, 97]
Collections.sort(list);
System.out.println(list);//[16, 19, 31, 44, 57, 60, 78, 85, 97, 98]
}
public void testSort2() {
//默认的自然排序:调用比较规则compareTo
List<String> names=new ArrayList<String>();
Collections.addAll(names, "Tom","Jerry","Andy","John");
System.out.println(names);//[Tom, Jerry, Andy, John]
Collections.sort(names);
System.out.println(names);//[Andy, Jerry, John, Tom]
}
2、 Comparator:自定义排序
一旦Java类实现了Comparable接口,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparable接口回调的方式。Comparable接口要求实现类必须重写其定义的方法:
--int compare(To1,T o2)
该方法的返回值要求:
--若o1>o2,则返回值应>0
--若o1<o2,则返回值应<0
--若o1==o2,则返回值应为0
利用方法:
Collections.sort(list,构造器)
就可以进行任意规则的排序
public void testSort3() {
//自定义的比较器:按照字符长度比较大小
Comparator<String> byLength=new Comparator<String>() {
public int compare(String o1,String o2) {
return o1.length()-o2.length();
}
};//内部类
String s1="abcde";
String s2="好好学习we";
System.out.println(byLength.compare(s1,s2));
List<String> names=new ArrayList<String>();
Collections.addAll(names, "Tom","Jerry","Andy","John");
System.out.println(names);//[Tom, Jerry, Andy, John]
//对names按照字符串长度比较大小进行排序,这是Andy和John谁在前面都可以,因为长度一样
Collections.sort(names, byLength);
System.out.println(names);//[Tom, Andy, John, Jerry]
//sort方法封装了排序算法,按照从小到大排序,是依照byLength中的compare()方法的结果比较大小排序
}
public void testSort4() {
//实现按字符串倒序排序的算法
String s1="abc";
String s2="zde";
//s1.compareTo(s2)<0
Comparator<String> byValue=new Comparator<String>() {
public int compare(String o1,String o2) {
return -(o1.compareTo(o2));//按照z到a的顺序排序
}
};
System.out.println(s1.compareTo(s2));//-25
System.out.println(byValue.compare(s1, s2));//25
List<String> list=new ArrayList<String>();
Collections.addAll(list, "abc","d","f","z");
System.out.println(list);//[abc, d, f, z]
Collections.sort(list, byValue);
System.out.println(list);//[z, f, d, abc]
}
public void testSort5() {
Comparator<Person> byAgeHeight=new Comparator<Person>() {
public int compare(Person o1, Person o2) {
先按照年龄比较,年龄如果相同就按照身高比较
if(o1.age==o2.age) {
return o1.height-o2.height;
}
return o1.age-o2.age;
}
};
//先按照年龄比较,年龄如果相同就按照身高比较
List<Person> list=new ArrayList<Person>();
list.add(new Person("Tom",4,35));
list.add(new Person("Jerry",10,22));
list.add(new Person("Andy",20,11));
list.add(new Person("Nemo",2,35));
list.add(new Person("Lee",4,20));
System.out.println(list);//[Tom:4:35, Jerry:10:22, Andy:20:11, Nemo:2:35, Lee:4:20]
Collections.sort(list, byAgeHeight);
System.out.println(list);//[Nemo:2:35, Lee:4:20, Tom:4:35, Jerry:10:22, Andy:20:11]
}
class Person{
String name;
int age;
int height;
public Person(String name,int age,int height) {
this.name=name;
this.age=age;
this.height=height;
}
public String toString() {
return name+":"+age+":"+height;
}
}
关于自定义排序:
1、Comparable可比较的,这个接口不常用,是默认比较规则接口,实现这个接口的类可以进行“自然排序”。Java API中有些类型实现了这个接口:String、包装类、时间等。实现这个接口必须保证:equals compareTo hashCode的一致性,当两个对象equals结果为true的时候,compareTo结果为0,同时具有相同的hashCode。
2、Comparator:比较器,用于实现某两个对象的自定义比较大小,没有限定,可以按照任何属性和算法比较,是常用的比较规律,可以利用这个比较器,进行任意的排序。
3、Java自带的排序算法都是升序排序,可以利用比较规则影响排序结果。
Collections:
Collections是集合的工具类,包含大量的集合工具方法(算法):填充Collections.addAll(),排序Collections.sort()、查找、二分查找等。去java2s.com这个网站中查看具体的。
队列和栈:
1、Queue
队列遵循先进先出原则,一端进,一端出。
JDK提供了Queue接口,同时使得LinkedList实现了该接口。选择LinkedList实现Queue的原因在于Queue经常要进行添加和删除的操作,而LinkedList在这方面效率高。
Queue接口主要方法如下:
1、boolean offer(E e):将一个对象添加至队尾,如果添加成功则返回true。
2、E poll():从队首删除并返回一个元素。
3、E peek():返回队首的元素(但不删除)。
4、queue.isEmpty();
5、queue.size();
public void testQueue() {
Queue<String> queue=new LinkedList<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
}
}
1、 Deque
Deque是Queue的子接口,定义了所谓的双端队列,即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。
2、 栈
如果将Deque限制为只能从一端入队和出队,则可实现栈(Stack)的数据结构,对于栈而言,入栈称之为push,出栈称之为pop。栈遵循先进后出的原则。
public void testStack() {
Deque<String> stack=new LinkedList<String>();
stack.push("a");
stack.push("b");
stack.push("c");
System.out.println(stack);//[c,b,a]
String str=stack.peek();//c
System.out.println(str);
while(stack.size()>0) {
str=stack.pop();
System.out.print(str+" ");//c b a
}
}
栈的方法:
1、stack.push();
2、stack.pop();
3、stack.peek();
4、stack.isEmpty();
5、stack.empty();
6、stack.size();