黑马程序员——java学习日记五

来源:互联网 发布:在淘宝上怎么退换货 编辑:程序博客网 时间:2024/06/05 09:12
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、集合概述
1 集合的存在是为了服务面向对象语言中的对象而存在的,为了方便对多个对象操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
2 集合与数组同是容器,区别在于:
(1)数组的长度是固定的,集合长度是可变的。
(2)数组中可以存储基本数据类型,集合只能存储对象。
(3)数组元素类型是统一的,而集合可以存储不同类型的对象。
3 集合的丰富性是因为底层所用的数据结构是各不相同的,各有特点。

二、集合基本内容
java集合类库将接口与实现分析,这有利于我们实现自己的类库,也方便使用他人编写的类库。
1 java类库中的Collection接口:
1,添加add(e);               //将一个元素添加到该集合中addAll(collection);   //将来自other集合中的所有元素添加到该集合中2,删除remove(e);            //删除该集合中与obj相等的对象removeAll(collection);//从该集合中删除在other集合存在的所有元素clear();              //清空集合3,判断。contains(e);          //如果该集合包含一个与obj相等的对象,返回trueisEmpty();            //如果该集合包含了other集合中的所有元素,返回true4,获取iterator();          //返回一个迭代器,用于访问集合中的各个元素size();              //返回当前存放在集合中的元素的数量5,获取交集。retainAll();        //用于从给定集合中移除与other集合中的任何元素都不相等的所有元素6,集合变数组。toArray();          //返回该集合的对象数据 返回类型是Object[]
这里强调一下:java集合类库中的迭代器与其他类库的迭代器在概念上有一个很重要的差别,一些语言比如C++的标准模板库汇总,迭代器是根据数据索引来建模的,不需要元素的查找操作,利用数组索引就可以将迭代器移动到下一个位置,但是,Java迭代器的查找操作与位置的变更是紧密相连的,查找一个元素的唯一方法就是调用next(),而执行该查找操作的同时会使迭代器的位置向前移动。因此,你可以将Java迭代器看成是位于各个元素之间的,当你调用next方法时,迭代器便越过下一个元素,并且返回它刚刚越过的那个元素的引用。

2 List:元素是有序的,这里有序是指元素存入顺序,元素可以重复
特有方法:凡是可以操作角标的方法都是该体系特有的方法。
增: add(index,element); addAll(index,Collection);    
删:remove(index);
改:set(index,element);
查: get(index): subList(from,to); listIterator(); int indexOf(obj); ListIterator listIterator();
 ListIterator是List集合特有的迭代器,是Iterator的子接口。
ListIterator:
增:add(E e); 
删:remove();
改:set(E e);
查:hasNext(); hasPrevious(); next();  previous(); nextIndex(); previousIndex()
2.1 ArrayList
底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
示例1:
class ListDemo{public static void sop(Object obj){System.out.println(obj);}public static void method(){ArrayList al = new ArrayList();//添加元素al.add("java01");al.add("java02");al.add("java03");sop("原集合是:"+al);//在指定位置添加元素。al.add(1,"java09");//删除指定位置的元素。al.remove(2);//修改元素al.set(2, "nihao");//通过角标获取元素。al.get(1);//获取所有元素。for(int x=0; x<al.size(); x++){sop(al.get(x));}Iterator it = al.iterator();while(it.hasNext()){sop(it.next());}//通过indexOf获取对象的位置。al.indexOf("java02");//获取子集合List sub = al.subList(1,3);sop("sub="+sub);}public static void listIterator_demo(){//演示列表迭代器。ArrayList al = new ArrayList();//添加元素al.add("java01");al.add("java02");al.add("java03");sop(al);//演示List特有的迭代器 ListIteratorListIterator li = al.listIterator();while(li.hasNext()){Object obj = li.next();if(obj.equals("java02"))//li.add("java009"); 添加和修改 而Iterator做不到  li.set("java006");}while(li.hasPrevious())//与hasNext()相对 从后向前{li.previous();}sop(li.hasNext());//下一个sop(li.hasPrevious());//上一个}public static void main(String[] args){<span style="white-space:pre"></span>method();}}
又一强调重点:我们要避免并发修改集合,遵循一个简单的原则------根据需要给容器附加许多的迭代器,但是这些迭代器都只能读取列表,单独创建一个既能读取又能写入的迭代器。关于迭代器是如何探测到并发修改集合的问题:查阅资料,得出这样一个原理------集合可以跟踪改写操作的数量,每一个迭代器都维护一个独立的计数值,记录着该迭代器负责的改写操作的数量,在每一个迭代器方法开始处,迭代器都要检查它自己额改写操作计数值和集合的改写操作计数值是否一致,如果不一致,它便抛出一个ConcurrentModification-Exception异常。
示例2:
/*去除ArrayList集合中的重复元素。*/class ArrayListTest{public static void sop(Object obj){System.out.println(obj);}public static void main(String[] args) {ArrayList al = new ArrayList();//对象一应该会被垃圾回收机制回收al.add("java01");al.add("java02");al.add("java01");al.add("java02");al.add("java01");sop(al);al = singleElement(al);sop(al);}public static ArrayList singleElement(ArrayList al){/*1 创建一个空的容器temp2 循环从al中取出一个元素,判断temp中是否含有该元素,如果有则丢弃,没有则放入temp中*///定义一个临时容器ArrayList newAl = new ArrayList();Iterator it = al.iterator();while(it.hasNext()){Object obj = it.next();if(!newAl.contains(obj)){newAl.add(obj);}}return newAl;}}

2.2 LinkedList
底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
示例:
/*LinkedList:特有方法:addFirst();addLast();getFirst();getLast();获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementExceptionremoveFirst();removeLast();获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException在JDK1.6出现了替代方法。offerFirst();offerLast();peekFirst();peekLast();获取元素,但不删除元素。如果集合中没有元素,会返回null。pollFirst();pollLast();获取元素,但是元素被删除。如果集合中没有元素,会返回null。 poll有修改的意思*/
//队列class MyQueue{private LinkedList link;//封装了一个链表MyQueue(){link = new LinkedList();}public void myAdd(Object obj){link.addFirst(obj);}public Object myGet(){return link.removeLast();}public boolean isNull(){return link.isEmpty();}}//堆栈class MyStack{private LinkedList link;MyStack(){link = new LinkedList();}public void myPush(Object obj){link.addFirst(obj);}public Object myPop(){return link.removeFirst();}public boolean isNull(){return link.isEmpty();}}

2.3 Vector
Vector是一种线程安全的集合,底层是数组数据结构,被ArrayList替代了,因为效率低。
枚举就是Vector特有的取出方式。发现枚举和迭代器很像。其实枚举和迭代是一样的。
因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。
示例:
class VectorDemo {public static void main(String[] args) {Vector v = new Vector();//创建一个集合v.add("java01");        //添加元素v.add("java02");v.add("java03");v.add("java04");Enumeration en = v.elements();//获得一个枚举对象while(en.hasMoreElements())   //进行元素迭代{System.out.println(en.nextElement());}}}

3 Set:元素是无序的(存入和取出的顺序不是一致的),元素不可以重复。该集合的功能和Collection是一致的。
3.1 HashSet
底层数据结构是哈希表,是线程不安全的。哈希表也就是散列表可以快速查找所需要的对象,它为每个对象计算出一个整数,成为哈希值,哈希值是以某种方法从对象的实例字段产生的整数。因此,具有不同数据字段的对象会产生不同的散列码。改集合保证数据唯一性的原理就是依赖元素的hashCode()方法和equals()方法,前者相同,才会判断后者是否相同,若两者均为true,则判定为相同对象。因此非常重要的一点,在定义自己的类时,需要实现相应的hashCode方法,并且,hashCode方法必须与equals方法相兼容:如果a.equals(b)为true,那么a和b必须有相同的散列码。
示例1:
/*往hashSet集合中存入自定义对象姓名和年龄相同为同一个人,重复元素。重写Person类中的hashCode() equals()方法*/class HashSetTest{public static void sop(Object obj){System.out.println(obj);}public static void main(String[] args) {HashSet hs = new HashSet();//创建一个set集合hs.add(new Person("a1",11));hs.add(new Person("a2",12));hs.add(new Person("a3",13));hs.add(new Person("a2",12));hs.add(new Person("a4",14));sop(hs.contains(new Person("a2",12)));//判断元素是否存在---truesop(hs.remove(new Person("a4",14)));//true 代表删除成功Iterator it = hs.iterator();while(it.hasNext()){Person p = (Person)it.next();sop(p.getName()+"..."+p.getAge());}}}class Person {private String name;//姓名字段private int age;    //年龄字段Person(String name, int age){this.name = name;this.age = age;}public int hashCode(){return name.hashCode()+age*47;//根据姓名和年龄算特有的哈希值}public boolean equals(Object obj)    //重写equals方法{if(!(obj instanceof Person)) //判断比较对象的类型是否属于Personreturn false;Person p =(Person)obj;return this.name.equals(p.name) && this.age == p.age;}public String getName(){return name;}public int getAge(){return age;}}
关于使用HashSet集合要非常注意一点:当你改变散列集中的元素时,如果被改变的元素的散列码也要改变,那么该元素在数据结构中的位置也要做相应的变化。否则非常容易造成内存的浪费。
示例2:
Collection collections = new HashSet();//创建一个根据对象哈希值进行存储的集合Person p1 = new Person("a1",11);Person p2 = new Person("a2",22);Person p3 = new Person("a3",33);collection.add(p1);collection.add(p2);collection.add(p3);p1.age = 44;//如果不注意,造成内存浪费,这样做会造成对象哈希值发生改变,无法定位对象在集合中的位置collections.remove(p1);//删除失败,并没有找到p1System.out.println(collection.size());//输出结果为3,p1并没有被删除

3.2TreeSet
可以对Set集合中的元素进行排序,集合底层是红-黑树(一种自平衡二叉查找树)。
该集合对元素进行排序的第一种方式是默认元素实现了Comparable接口,覆盖compareTo方法,这种方式也称为按元素的自然顺序排序。第二种方式:给集合对象传入一个比较器对象,也就是实现了Comparator接口的类对象。
非常重要的一点是:实现比较方法时,当主要条件相同时,一定判断一下次要条件
示例:
/*练习:按照字符串长度排序。字符串本身具备比较性。但是它的比较方式不是所需要的。这时就只能使用比较器。*/import java.util.*;class  TreeSetTest{public static void main(String[] args) {TreeSet ts = new TreeSet(new StrLenComparator());//创建一个集合ts.add("abcd");//向集合中添加元素ts.add("cc");ts.add("cba");ts.add("aaa");ts.add("z");ts.add("hahaha");Iterator it = ts.iterator();//遍历集合while(it.hasNext()){System.out.println(it.next());}}}class StrLenComparator implements Comparator//自定义比较器类{public int compare(Object o1,Object o2)//比较两个字符串对象{String s1 = (String)o1;String s2 = (String)o2;/*if(s1.length()>s2.length())return 1;if(s1.length()==s2.length())return 0;*///将字符串按长度进行比较int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));if(num==0)return s1.compareTo(s2);//当长度相等时,就按字符自然顺序进行排序return num;}}


4 Map集合:该集合存储键值对,而且要保证键的唯一性 
Java类库为该接口提供两个通用实现:即HashMap和TreeMap,HashMap对键进行散列,而TreeMap则使用键的全局顺序进行排序,并将其组织成搜索树,散列函数或比价函数只能作用于键,与键关联的值不能进行散列或者比较。
键必须是唯一的,我们不能为同一键存储两个值,如果我们使用同一个键调用两次put方法,那么第二个值将取代第一个值,并且put方法返回的是上一个存储的值。
该集合有两种取出方式:1 将map中所有的键存入到Set集合。因为set具备迭代器。所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。2 将map集合中的映射关系存入到了set集合中,利用set的迭代器,循环取出
一对键值对,利用Map.Entry的方法取出键和值。
Map接口中定义的方法:
 1,添加:        put(K key, V value)        putAll(Map<? extends K, ? extends V> m)            2, 删除        clear()        remove(Object key)            3, 判断        containsValue(Object value)        containsKey(Object Key)        isEmpty()            4, 获取        get(Object key)        size()        values()             entrySet()        keySet()

4.1  HashMap
底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的,将hashtable替代。
示例:
class MapDemo2 {public static void main(String[] args) {Map<String,String> map = new HashMap<String,String>();map.put("02","zhangsan2");map.put("03","zhangsan3");map.put("01","zhangsan1");map.put("04","zhangsan4");//方法1 将Map集合中的键值对取出放入Set集合中Set<Map.Entry<String,String>> st = map.entrySet();Iterator<Map.Entry<String,String>> it = st.iterator();while(it.hasNext()){Map.Entry<String,String> m = it.next();System.out.println(m.getKey()+"..."+m.getValue());}//方法2 将Map集合中的键取出放入Set集合中 利用迭代器取出键 然后利用get(Object key)方法获取值Set<String> st2 = map.keySet();Iterator<String> it2 = st2.iterator();while(it2.hasNext()){String key = it2.next();String value = map.get(key);System.out.println(key+"..."+value);}}}

4.2 TreeMap
底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。  
示例:
/*练习:"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。希望打印结果:a(1)c(2).....通过结果发现,每一个字母都有对应的次数。说明字母和次数之间都有映射关系。有映射关系时,可以选择map集合。思路:1,将字符串转换成字符数组。因为要对每一个字母进行操作。2,定义一个map集合,因为打印结果的字母有顺序,所以使用treemap集合。3,遍历字符数组。将每一个字母作为键去查map集合。如果返回null,将该字母和1存入到map集合中。如果返回不是null,说明该字母在map集合已经存在并有对应次数。那么就获取该次数并进行自增。,然后将该字母和自增后的次数存入到map集合中。覆盖调用原理键所对应的值。4,将map集合中的数据变成指定的字符串形式返回。*/import java.util.*;import java.io.*;class  MapTest3{public static void main(String[] args) throws IOException{//1 键盘读取字符串BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));String line =null;while((line = bufr.readLine())!=null){String s = charCount(line);System.out.println(s);}bufr.close();}/*统计一个 字符串中字母出现的次数*/public static String charCount(String str){char[] arr = str.toCharArray();// 将字符串变成字符数组TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();//创建一个集合int count = 0;for(char ch : arr)//循环字符数组{if( !(ch>='a' && ch<='z' || ch>='A' && ch<='Z'))//判断字符是否为英文字母continue;Integer value = tm.get(ch);//将字符作为键值在集合中查找对应的值if(value!=null)//如果没有差到,就把字符作为键,1作为值存入集合中count = value;count++tm.put(ch,count);count=0;/* if(value == null){tm.put(ch,1);}else{++value;tm.put(ch,value);} */}StringBuilder sb = new StringBuilder();Set<Character> st = tm.keySet();Iterator<Character> it = st.iterator();while(it.hasNext()){char key = it.next();int value = tm.get(key);sb.append(key+"["+value+"] ");}return sb.toString();//输出集合中结果}}

5 Collections:集合框架的工具类。里面定义的都是静态方法。提供的方法中有可以对list集合进行排序,二分查找等方法。通常常用的集合都是线程不安全的,如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
示例:
//创建一个List集合List<String> list = new ArrayList<String>();list.add("abcd");list.add("aaa");list.add("zz");list.add("kkk");list.add("qq");list.add("z");//先对集合进行排序Collections.sort(list);//对集合进行指定元素的二分查找int index = Collections.binarySearch(list,"aaa");//返回集合中最大的元素String max = Collections.max(list);//返回集合中最小的元素String min = Collections.min(list);//使用默认随机源对指定列表进行置换。Collections.shuffle(list);//在排序时使用比较器,那么在二分查找的时候将比较器一起带入Collections.sort(list,new StrLenComparator());int index = Collections.binarySearch(list,"aaa",new StrLenComparator());


0 0
原创粉丝点击