java学习第16天--集合2

并发修改异常
package com.czz.test00;
 
import java.util.ArrayList;
import java.util.Iterator ;
import java.util.ListIterator;

/*
 * 并发修改异常
 * 迭代器遍历集合时 , 却使用集合本身的方式修改集合元素 , 将会导致并发修改异常
 * */  
public class ConcurrentExcepDemo {
 
   public static void main(String[] args ) {
      ArrayList<String> list = new ArrayList<String>();
 
      list .add( "hello" );
      list .add( "hello" );
      list .add( "niling" );
      list .add( "czz" );
 
      // 迭代器遍历
/*    Iterator<String> it = list.iterator();
      while (it.hasNext()) {
         String next = it.next();
         if (!next.startsWith("h")) {
            // list.remove("hello");//java.util.ConcurrentModificationException
            it.remove();
         }
         System.out.println(next);
      }
*/
     
      // 集合本身的方式遍历 , 集合本身方式修改
/*    for ( int i = 0; i < list.size(); i++) {
         if((list.get(i)).equals("hello")) {
            list.remove(i);
            i--;
         }
         System.out.println(list);
      }
*/    
     
      //ListedList 类底层使用的是链表保存数据
      ListIterator<String> li = list .listIterator();
      while ( li .hasNext()) {
         String str = li .next();
         if ( str .equals( "hello" )) {
            li .add( "HELLO" );
         }
      }
     
      // 遍历
      for (String str : list ) {
         System. out .println( str );
      }
   }
}
 
练习:
使用LinkedList类模拟栈结构的集合.(FILO,LIFO)
注意是模拟栈结构,不是直接使用LinkedList,即:有一个集合,其中的元素是先进后出.
package com.czz.test01;
 
import java.util.LinkedList;
 
// 自定义栈
class MyStack { 
   LinkedList list = new LinkedList ();
  
   public void add(Object obj ) {
      list .addFirst( obj ) ;
   }
  
   public Object get() {
      Object obj = list .remove(); //remove 获取并移除此列表的头 ( 第一个元素 )
      return obj ;
   }
  
   public boolean isEmpty() {
      return list .isEmpty();
   }
}
 
public class FILIDemo{
   public static void main(String[] args ) {
      MyStack link = new MyStack();
     
      link .add( "hello" );
      link .add( "hello1" );
      link .add( "hello2" );
      link .add( "niling" );
     
      while (! link .isEmpty()) {
         System. out .println( link .get());
      }
   }
}

泛型
概述:
1. JDK1.5 之前,把对象放入到集合中,集合不会记住元素的类型,取出时,全都变成 Object 类型
2. 集合接口,集合类中出现的 <> 就是泛型,即参数化类型 <> 中的字母代表的是类型
3. 提高了程序的安全性,不符合类型的元素不能添加
4. 将运行期遇到的问题转移到了编译期
5. 记住了元素的类型,取出元素时省去了类型强转的麻烦

泛型类的定义
在类上添加一个类型的定义,在使用类时指定一个类型,就可以对传入的数据进行类型检查;在取出时,不必进行类型转换了,这样的类就叫做泛型类.

泛型类的特点:
每次针对不同类型的参数,都必须创建不同类型的对象;
这使得方法依赖于创建对象时的类型,他们之间的耦合度太高了;

泛型方法的定义
为了降低这种耦合度,可以把泛型定义在方法上,这就是泛型方法


例:
package com.czz.fanxing;
 
// 自定义泛型类
class Genclass<T>{
   private T num ;
 
   // 自定义泛型方法
   public T getNum() {
      return num ;
   }
 
   public void setNum(T num ) {
      this . num = num ;
   }
}
 
public class GenericDemo {
 
   public static void main(String[] args ) {
      Genclass<String> gen1 = new Genclass<String> ();
      gen1 .setNum( "2392" );
      System. out .println( gen1 .getNum());
   }
}
 
或者自定义泛型方法(在返回值前面声明)
class czz{
   public <T> void show(T t ) {     //一旦方法是泛型方法,请在返回值前需要做泛型声明
   }
}

泛型接口的定义
实现类实现泛型接口时,有两种情况:
1.实现类还不知道接口上应该传递什么类型,那么此实现类依然是个泛型类;
2.实现类知道了接口上应该传递什么类型,那么此实现类就是一个普通类.

例:
package com.czz.fanxing;
 
// 定义泛型接口
interface InterA<T>{
   public abstract void show(T t );
}
 
// 接口实现类
// 实现类知道接口中的具体类型 , 此时实现类可以是非泛型类
class A implements InterA<String>{
   @Override
   public void show(String t ) {
      // TODO Auto-generated method stub
      System. out .println();
   } 
}
 
// 实现类在实现接口时 , 本身还是一个泛型类
class B<T> implements InterA<Integer>{
   @Override
   public void show(Integer t ) {
      // TODO Auto-generated method stub
   } 
}
 
// 实现类也不知道接口中泛型的具体类型 , 那么子类也是一个泛型类
class C<T> implements InterA<T>{
   @Override
   public void show(T t ) {
      // TODO Auto-generated method stub  
   } 
}
 
public class GenericDemo2{
   public static void main(String[] args ) {   
   }
}
 
演示子父类关系中,子类继承泛型父类时的三种情况:
1.子类知道父类的泛型类型
2.子类不知道父类的泛型类型
3.子类知道父类的泛型类型,同时子类又是一个泛型类
package com.czz.fanxing;
 
// 抽象类
abstract class Father<T>{
   public abstract void show(T t );
}
 
// 子类知道父类的泛型类型 , 但子类不是泛型
class Son1 extends Father<String>{
   @ Override
   public void show(String t ) {
      // TODO Auto-generated method stub
   } 
}
// 子类知道父类的泛型类型 , 但子类是泛型
class Son2<T> extends Father<String>{
   @ Override
   public void show(String t ) {
      // TODO Auto-generated method stub
   }
}
 
// 子类不知道父类的泛型类型 , 同时子类也是一个泛型类
class Son3<T> extends Father<T>{
   @ Override
   public void show(T t ) {
      // TODO Auto-generated method stub
   }
}
 
public class GenericDemo6 {
   public static void main(String[] args ) {
      // TODO Auto-generated method stub
   }
}
 


 集合中泛型的完整使用
其实集合是泛型的最主要的使用场景,正常的集合在使用时,都应该指定其中元素的类型, ArrayList<String> al = new ArrayList<String>();

带泛型版ArrayList的使用
练习:
1.存储基本数据类型的元素并遍历
2.存储字符串元素并遍历
3.存储自定义类型的对象并遍历
package com.onnez.code;

import java.util.ArrayList;
import java.util.Iterator;

class A{
private String a;
public String getA() {
return a;
}
public void setA(String a) {
this.a=a;
}
public A() {
super();
// TODO Auto-generated constructor stub
}
public A(String a) {
super();
this.a = a;
}
public String toString() {
return a;
}
}

public class GenerictyDemo {

public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();

list.add("hello");
list.add("world");

Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}

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

ArrayList<A> list1=new ArrayList<A>();

list1.add(new A("HELLO"));
list1.add(new A("WORLD"));

Iterator<A> it1 = list1.iterator();
while(it1.hasNext()) {
System.out.println(it1.next());
}
}
}


泛型通配符
通配符 :
在创建集合变量时 , 不知道具体的类型 , 但是知道其类型的范围 , 可以使用类型通配符 ? 表示一个不确定的类型
和其配套使用的还有以下两种 :
? extends E: 表示 E 及其子类
? super E: 表示 E 及其父类
例:
? extends Animal : 上限限定,Animal和它的子类可以使用
? super Animal: 下限限定,Animal和它的父类都可以使用

例:
/*
* 泛型类是没有多态性
* 泛型如何实现类似于多态性?
* 通配符能实现
* ?
* ? extends Animal : 上限限定,Animal和它的子类可以使用
* ? super Animal: 下限限定,Animal和它的父类都可以使用
*
* 类型限定的使用
*
*/
class Animal {

}

class Dog extends Animal {

}

class Cat extends Animal {

}

class Demo<T> {

}

class DemoTest {
public void test6(Collection<? extends Animal> c){//
//由于不知道具体的类型,所以不能放东西
// c.add(null);
//由于元素的类型要么是Animal,要么是它的子类,都可以使用Animal去接
for (Animal animal : c) {
}
}

public void test7(Collection<? super Animal> c){//
//由于不知道具体的类型,但是我知道都是Animal或者是它的父类,Animal和它的子类可以自动转成Animal
c.add(new Animal());
c.add(new Dog());
c.add(new Cat());
// c.add(new Object());
//由于不知道类型的上限,所以不能往外拿元素

// for (Object object : c) {
//
// }

}


public void test(Demo<Animal> d) {// 多态?Demo<Dog>

}

public void test2(Demo<?> d) {// 相当于Object

}

public void test3(Demo<Object> d) {// 相当于Object

}

public void test4(Demo<? extends Animal> d){//Animal或者它的子类

}
public void test5(Demo<? super Animal> d){//Animal或者它的父类都可以使用
}
}

public class GenericDemo1 {

public static void main(String[] args) {
DemoTest dt = new DemoTest();

dt.test5(new Demo<Animal>());
dt.test5(new Demo<Object>());
// dt.test5(new Demo<Dog>());//NG

// dt.test4(new Demo<Animal>());
// dt.test4(new Demo<Dog>());
// dt.test4(new Demo<Cat>());
// dt.test4(new Demo<Object>());//NG

// dt.test2(new Demo<Integer>());

// Demo<Dog> d = new Demo<Dog>();
// Demo<Animal> d2 = new Demo<>();

// dt.test(d2);//Demo<Animal> Demo<Dog>

}

}




Set接口概述
Set接口:一个不包含 "重复元素"的集合
Set接口中并没有定义特殊的方法,其方法多数都和collection接口相同

哈希集合中对重复元素的理解:和通常意义上的理解是不一样的.两个元素(对象)的hashcode返回值相同,并且equals返回值为true时(或者地址相同时),才称这两个元素是相同的.

HashSet:哈希集
线程不安全,存取速度快
他的方法大多数和collection相同
它不保证元素的迭代顺序,也不保证该顺序恒久不变
当HashSet中的元素超过一定数量时,会发生元素的顺序重新分配

设计程序完成:对同一个对象可以多次添加到HashSet集合中
package com.czz.hashcode;

import java.util.HashSet;

class Student{
String name;
static int a = 0 ;
public Student(String name) {
this.name = name;
}

@Override
public int hashCode() {
System.out.println(this.name + " hashCode()...");//toString
return a++;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return true;
}
}

public class HashCodeDemo1 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student s1 = new Student("s1");
Student s2 = new Student("s2");
Student s3 = new Student("s3");
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s3);
set.add(s3);
System.out.println(set.size());
}
}

存储自定义类对象,并遍历.当所有的成员变量都相同,则为相同对象
package com.czz.hashcode;

/*
* 存储自定义类对象,并遍历.当所有的成员变量都相同,则为相同对象
* */

import java.util.HashSet;
import java.util.Iterator;

class student1{
private String name;
private int age;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public student1(String name) {
super();
this.name = name;
}

public student1() {
super();
// TODO Auto-generated constructor stub
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
student1 other = (student1) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

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

public class HashCodeDemo2 {

public static void main(String[] args) {
HashSet<student1> set = new HashSet<>();
student1 s1 = new student1("czz",25);
student1 s2 = new student1("nl",23);
student1 s3 = new student1("czz",25);
set.add(s1);
set.add(s2);
set.add(s3);
for (student1 student1 : set) {
System.out.println(student1.getName() + "," +student1.getAge());
}
System.out.println(set.size());
}

}

产生10个1-20之间不同的随机int值,要求使用HashSet集合
package com.czz.hashcode;
 
import java.util.HashSet;
 
public class HashCodeDemo3 {
 
   public static void main(String[] args ) {
      HashSet<Integer> set = new HashSet<>();
     
      while ( set .size() <= 10) {
         int i = ( int )(Math. random ()*20 +1);
         set .add( i );
      }
     
      for (Integer integer : set ) {
         System. out . println ( integer );
      }
   }
}

HashSet如何保证元素的唯一 
考查add(Object obj)方法的实现过程
1.先调用obj的hashCode方法,计算哈希值(槽位值)
2.根据哈希值确定存放的位置
3.若位置上没有元素,则这个元素就是第一个元素,直接添加
4.若此位置上已经有元素,说明还有元素的hashCode方法返回值与它相同,则调用它的equals方法与已经存在的元素进行比较
5.若返回值为true,表明两个元素是“相同”的元素,不能添加
6.若返回值为false,表明两个元素是“不同”的元素,新元素将以链表的形式添加到集合中

class Student{
String name;
public Student(String name) {
this.name = name;
}

@Override
public int hashCode() {
System.out.println(this.name + " hashCode()...");//toString
return 0;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return false;
}
}


public class HashSetDemo {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student s1 = new Student("s1");
Student s2 = new Student("s2");
Student s3 = new Student("s3");
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s3);
set.add(s3);
System.out.println(set.size());
/*
HashSet<String> set = new HashSet<String>();
set.add("hello");
set.add("hello");
set.add("hello");
set.add("hello2");
set.add("hello2");

for (String string : set) {
System.out.println(string);
}
System.out.println(set.size());
*/
}
}

HashSet注意事项:
1.想要往HashSet中添加对象,需要在定义类时,重写hashCode和equals方法
2.由于HashSet使用的是散列方法,所以,轻易不要在迭代集合元素的时候改变集合中的元素

LinkedHashSet类
从后缀可以看出 : 其本质是 HashSet, 只不过在内部维护了一个链表 , 可以记住元素放入的顺序 , 这样就保证了存取的顺序 , 但是 , 正是由于多了链表 , 所以它的效率低些 .




猜你喜欢

转载自blog.csdn.net/czz1141979570/article/details/80081892