Thinking in Java 9

zhaozj2021-02-11  231

Chapter 8 Acquisition

"If a program only contains a fixed object, and it is known that the existence time is, then this program can be said to be quite simple."

Usually, our program needs to create new objects based on some of the criteria that they know when running. If the non-program is officially running, we don't know how much objects you need, and even don't know their exact type. In order to meet the needs of routine programming, we are asked to create any number of objects at any time. So you cannot rely on a named handle to accommodate your own object, just like this:

MyObject myhandle;

Because I don't know how much this is actually needed.

To solve this very critical problem, Java provides a variety of ways of accommodating objects (or handles). The built-in type is an array, we have discussed it before, this chapter is ready to deepen everyone's understanding. In addition, Java's tool (utility) library provides some "collection classes" (also known as "container classes", but the term has been used by AWT, so it is still using the "collection".). Using these collection classes, we can accommodate or operate their objects. The remainder of this chapter will discuss this in detail.

8.1 array

Most of the necessary introductions to array have been conducted in the last section of Chapter 4. Through the study there, everyone knows how to define and initialize an array. The accommodation of the object is the focus of this chapter, and the array is just a way to accommodate the object. However, because there are other large numbers of methods to accommodate arrays, what is the other groups so special?

There are two questions to distinguish between arrays from other collection types: efficiency and type. For Java, for saving and accessing a series of objects (actually object handles) array, the most effective method is more than an array. The array actually represents a simple linear sequence, which makes the elements are very fast, but we have to pay for this speed: When you create an array object, its size is fixed, and it is not available in that array object. " There is a change in time. You can create an array of specific sizes, then if you use light storage space, create a new array, move all handles from the old array to the new array. This is a "vector" class behavior, which will also discuss it in detail later. However, due to the flexibility of this size, we believe that the efficiency of the vector does not have an array.

The C vector class knows what type of object that it is accommodated, but it has a significant shortcomings compared to the Java array: C vector class Operator [] cannot perform a range check, so it is easy to exceed the boundary (however, It can query how large, and the AT () method does perform a range check). In Java, no matter the array or collection, the range check - If the boundary is exceeded, you will get a RuntimeException error. As everyone learned in Chapter 9, such violations pointed out that a programmer is wrong, so it is not necessary to check it in the code. On the other hand, since the VECTOR of C does not perform a range check, the access speed is faster - in Java, the performance has a certain impact on the performance of the array and the collection.

This chapter also studies several other common collection classes: Vector (vectors), stack, and havehtable. These classes involve processing of objects - as if they don't have a specific type. In other words, they treat them as the Object type (the Object type is "root" class in Java. From a point of view, this processing method is very reasonable: We only need to build a collection, then any Java object can enter that collection (except for the basic data type - the basic type package class of the available Java as a constant Place a collection, or encapsulate it into its own class, as used as a variable value). This once again reflects the array of conventional set: When creating an array, it can accommodate a specific type. This means that the compile period type check is performed, preventing the type of incorrection, or error specifying the type of preparing for extraction. Of course, in the compile period or running period, Java will prevent us from sending improper messages to an object. So we don't have to consider which practice is more dangerous. As long as the compiler can point out errors in time, the speed is accelerated during operation, and the purpose is also achieved. In addition, users rarely be very surprised by a violation event. Considering the performance efficiency and type check, arrays should be used as much as possible. However, when we try to solve a more conventional problem, the limitations of arrays may also be very obvious. After studying over the array, the remaining parts of this chapter will focus on the collection of Java provided.

8.1.1 arrays and first types of objects

Regardless of the array of uses belongs, the array identifier is actually a handle to the real object. Those objects themselves are created in the memory "heap". The heap object can be created (ie, the default), or "explicitly" creation (ie, specify, with a new expression). Part of the heap (actually the unique field or method we can access) is a read-only Length member, which tells us how many elements can be accommodated in that array object. For array objects, "[]" syntax is the only alternative access method we can adopt.

The following example shows different ways to initialize the array, and how to assign array handles to different array objects. It also reveals that an array of object arrays and basic data types is almost entirely exactly in use. The only difference is that the object array is to accommodate the handle, and the basic data type array is accommodated for specific values ​​(if you have difficulty in performing this program, please refer to Chapter 3 "Assignment" section):

325-327 page program

Among them, array a is only initialized into a NULL handle. At this point, the compiler disables us to do any actual operation to this handle unless it is initialized. Array B is initialized to point to an array that is composed of a Weeble handle, but does not place any Weeble objects in that array. However, we can still query the size of the array because B points to an legitimate object. This also brought us a problem: You can't know how many elements in that array actually contains, because Length only tells us how much elements can be placed into that array. In other words, we only know the size or capacity of array objects, and I don't know how many elements do actually accommodate. Despite this, since the array object is automatically initialized into NULL at the beginning of the creation, it can be checked whether it is null, and it is determined whether a particular array "vacancy" is to accommodate an object. Similarly, an array composed of basic data types is automatically initialized into zero (target type), NULL (character type), or false.

Array C shows that we first create an array object, and then assign a Weeble object to all "vacancy" of the array. The array D reveals the "set initialization" syntax to create an array object (clearly in the new command, similar to the array c), and then initialize with a Weeble object, all work in a statement. The following expression:

A = D;

To us show how to get the handle of the same array object connection, then assign it to another array object, just like any of the other types of object handles. Now, both A and D point to the same array objects in the memory stack.

Java 1.1 joins a new array initialization syntax, you can imagine it as "dynamic set initialization". The Java 1.0 set initialization method used by D must be performed while defining D. But if you use Java 1.1 grammar, you can create and initialize an array object anywhere. For example, suppose the hide () method is used to get an array of Weeble objects, then the traditional method is:

HIDE (D);

However, in Java 1.1, you can also dynamically create arrays that want to be passed as parameters, as shown below:

Hide (new week [] {new weeble (), new way ()});

This new grammer makes us more convenient to write code in some occasions.

The second portion of the above example reveals such a problem: for arrays composed of basic data types, their operation mode is extremely similar to the object array, just the former directly encapsulated the basic type of data value.

1. Basic data type collection

The set class can only accommodate the object handle. But for an array, it can make it directly to accommodate the basic type of data, or to accommodate the handle of the object. Use the "Packaging" class like Integer, Double, you can place the value of the basic data type into a collection. But as this chapter will say in the WordCount.java example, the package used for basic data types can only be played under certain occasions. Regardless of the basic type of data into an array, it is also related to the problem of performing efficiency within a class that enables its encapsulation. Obviously, if you can create and access a basic data type array, the former's efficiency will be much higher than the collection of access to a packaging data.

Of course, if you prepare a basic data type, you want the flexibility of the collection (automatic extension when needed, make more spaces), should not use arrays, a collection of data consisting of packaged data must be used. . Everyone may believe that there should be a special type of Vector for each basic data type. But Java did not provide this feature. Some formive modeling mechanisms may help Java better solve this problem a certain day (annotation 1).

1: This is a place for C than Java because C provides support for "parameterization type" through the Template keyword.

8.1.2 Return of array

Assume that we want to write a method now, and don't want it to return just the same thing, but want to return a series of things. At this point, language like C and C will make problems complicate because we cannot return an array and can only return a pointer to the array. This is very trouble, because it is difficult to control the "existing time" of the array, it is easy to cause memory "vulnerability".

Java uses a similar approach, but we can "return a array". Of course, the actual still returned at this time is the pointer to the array. But in Java, we will never worry about whether the array is available - as long as needed, it will automatically exist. Moreover, the garbage collector will automatically remove it after we finish.

As an example, please think about how to return a string:

329-330 page program

The Flavorset () method created a String array called Results. The size of the array is N - the specific value depends on the self-variable we pass to the method. Subsequently, it randomly selects some "Flavor" in the array FLAV and places them into results and eventually returns Results. Returning arguments with any difference between any other object - the end returned is a handle. As for the array, is it created in Flavorset (), or is created in other parts, this problem is not important because any handle is only one handle. Once our operation is completed, the garbage collector will automatically turn off the unpackage of the array. And as long as we need arrays, it will listen to the dispattion.

On the other hand, it is noted that when Flavorset () is randomly selected, it needs to ensure that random choices that have previously appeared will not appear again. In order to achieve this, it uses an infinite While loop to make a random selection until an element that does not appear in the picks array (of course, you can also compare the strings, check if the random selection is in the results array Over, but string comparison is relatively low). If successful, add this element and interrupt the cycle (BREAK), then find the next one (I value increment). But if T is an array that has appeared in the PICKS, use the tab of Continue to jump two-level, forcibly select a new T. Use a debugger to see this process very clearly.

Main () can show 20 complete spices collection, so we see Flavorset () to select spices in a random order each time. In order to experience this, the easiest way is to enter the output redirect into a file, then watch the content of this file directly.

8.2 Collection

Now summary what we have learned earlier: To accommodate a set of objects, the most suitable choice should be an array. And if you accommodate a series of basic data types, arrays must be used. In this chapter, everyone will come into contact with some more conventional situations. When we write a program, it is usually not exactly how many objects that eventually needed. Sometimes I even want to save objects in more complicated ways. To solve this problem, Java provides four types of "set classes": Vector, BitSet, Stack (stack), and HashTable. Compared to other languages ​​with collection functions, although the number of this is quite small, they can still use them to solve the quantity of amazing practical problems.

These set classes have a variety of characteristics. For example, Stack implements a LIFO (first in first out) sequence, and HashTable is an "associated array" that allows us to associate any objects. In addition, all Java set classes can automatically change their size. So, we can use many objects when programming, and don't worry that it will make a collection.

8.2.1 Disadvantages: Type Unknown

"Disadvantages" using Java collection is to lose type information when the object is placed in a collection. This happens, is due to the current preparation of the collection, the collection of programmers did not know what type of type of type was put on. If a collection only allows only a particular type, it hinders it to become a "conventional use" tool to bring trouble. To solve this problem, the collection actually accommodates the handle of some objects of Object. This type of course represents all objects in Java because it is the root of all classes. Of course, it is also important to note that this does not include basic data types because they are not inherited from "anything". This is a good solution, just not applicable: (1) When an object handle is placed in a collection, the type information will be abandoned, so any type of object can enter our collection - even special instructions It can only accommodate specific types of objects. For example, although it can only accommodate a cat, in fact, anyone can throw a dog.

(2) Since the type information does not exist, the only thing that the collection can affirm that it is to accommodate the handle of an object. Before formally use it, it must be made to make it a correct type.

It is gratifying that Java does not allow people to abuse the objects placed in the collection. If you throw a dog into a collection of a cat, then all things in the collection are as cats, so when you use that dog, you will get a "violation" error. In the same sense, if you try to "shape" to a cat, you will still get a "violation" error during operation.

Here is an example:

332-333 page program

It can be seen that the use of Vector is very simple: first create one, then use addElement () to get those objects with Elementat (), you have a size () method, so we know that it has been added. How many elements in order to prevent misalignment, causing a violation error).

The Cat and Dog classes are very shallow - except for "objects", they are not particularly, if they don't clearly point out what kind of inheritance, the default is to inherit from Object. So we can not only use the Vector method Objects are placed in this collection, but also add DOG objects, and do not get any error prompts in the compile period and running period. When you use the Vector Method Elementat () Get the object that is thought to be CAT, it is actually pointing to an Object handle. It is necessary to shape the object to CAT. Subsequently, the entire expression is needed to be enclosed with parentheses, and the forced shape is performed before the print () method is called; otherwise, a syntax error will appear. During the run, if you try to put DOG Object modeling is CAT, it will get a violation.

The meaning of these processing is very far. Although it is a bit trouble, it has gained a safe guarantee. We have never hidden in some hidden mistakes. If a part of the program is inserted into a collection, we just find a collection in a part of the program, you must find the location where the insertion error is placed. Of course, you can reach this by checking the code, but this is perhaps the most stupid debug tool. On the other hand, we can start our program from some standardized set classes. Although they have some shortcomings in terms of function, they are somewhat awkward, but they can guarantee that there is no hidden mistake.

1. Error sometimes does not reveal

In some cases, the program seems to work correctly, not style back to our original type. The first case is quite specialty: String class gets additional help from the compiler, making it work normally. As long as the compiler is looking forward to a String object, but it does not get one, it will automatically call in Object, and can be overwritten by any Java class. This method can generate String objects that meet the requirements and then use it when we need it. Therefore, in order to make your own objects can be displayed, all things to do is to override the toString () method, as shown in the following example:

334-335 page program

You can see the redefine code for TString () in mouse. In the second for cycle of Main (), the following statement can be found:

System.out.println ("Free Mouse:"

MICE.Elementat (i));

After " ", the compiler is expected to see a String object. Elementat () generates an Object, so the compiler will call toString by default for the String String. But unfortunately, only any types of such conversion are only for String.

The second way to hide the model has been applied in mousetrap. The caugha () method is not a mouse, but an Object. Then it will shape it into a mouse. Of course, this is very shot because anything can be passed to the method by receiving an Object. However, if the shape is incorrect - if we pass the type of error - will get a violation error during the run. This is of course not checked during the compile period, but still prevents problems. Note that you do not need to shape when using this method:

MouseTrap.caugha (mice.ementat (i));

2. Generate the Vector of automatic discrimination type

Everyone may not want to give up that question just now. A more "robust" program is to create a new class with a Vector so that it only receives the types we specified, and only the type we want. As follows:

335-336 page program

The previous example is similar, but the new GopherVector class has a type of VECTOR private member (inherits some trouble from Vector, reasoning to know later), and the method is similar to Vector. However, it does not receive and generate ordinary objects, only interested in Gopher objects.

Since GopherVector only receives a gopher (icy), if we use:

Gophers.addelement (New Pigeon ());

A error message will be obtained during compilation. In this way, although it looks more dull from the point of view of the encoding, it can be immediately determined whether or not the correct type is used.

Note that you don't have to make shape when using Elementat () - it is definitely a gopher.

3. Parameterization type

Such problems are not isolated - we don't have to create new types on other types. At this point, it is very helpful in compilation. This is the concept of "parameterization type". In C , it has been directly supported by the language through "template". At least, Java retains keyword Generic, expects one day to support parameterization types. But we can't determine when will this day will come.

8.3 enumerator (repetition)

In any set class, you must use some way to place an object, and use another method to obtain an object. After all, accommodating a variety of objects is the top priority of the collection. In the vector, addElement () is the method we use in inserted objects, and Elementat () is the only way to extract the object. Vector is very flexible, we can choose anything at any time and you can use different indexes to select multiple elements. If you look at this problem from a higher angle, you will find a defect in it: you need to know the exact type of the collection in advance, otherwise you will not be available. At first glance, this seems to have no relationship. However, if you start to use the Vector, you will then decide on the program to change (consider the reason for performing efficiency) to a list (part of the Java1.2 collection library), what should I do?

This can be used to achieve this purpose with the concept of "Iterator". It can be an object that is traversing a series of objects and selects each object in that sequence, and does not let the customer programmer know or pay attention to the infrastructure of the sequence. In addition, we usually believe that the repetition is a "lightweight" object; that is, create it only pays very little price. But it is precisely because of this reason, we often find that there are some strange restrictions. For example, some repelors can only move in one direction.

Java's enumeration (enumeration, note 2) is an example of a repetition with these restrictions. In addition to these, it is no longer used anything else:

(1) Use a method called Elements () to provide a collection to provide us with an enumeration. When we first call NEXTELEMENT (), this enumeration will return the first element in the sequence.

(2) Get the next object with nextElement ().

(3) Check if there is more objects in the sequence with a HASMEELEMENTS ().

2: "Recurrent" The word often appears in C and OOP, so it is difficult to determine why Java developers use such a strange name. Java 1.2's collection library revised this issue and many other questions.

You can only do these things with Enumeration, you can't have more. It belongs to a simple implementation of a repetition, but the function is still very powerful. In order to experience its operation process, let's review the CATSANDDOGS.JAVA program mentioned earlier in this chapter. In the original version, the Elementat () method is used to select each element, but in the following revisions, you can see a "enumeration":

338-339 page program

We see that the only change is the last few lines. Is no longer:

For (int i = 0; i

((CAT) CATS.Elementat (I)). Print ();

Instead, use an enumeration to traverse the entire sequence:

While (E.hasMoreElements ())

((CAT2) E.NEXTELEMENT ()). Print ();

Using ENUMERATION, we don't have to care about the number of elements in the collection. All work is taken by HasmoreElements () and nextElement ().

Let's take a look at another example, let us create a general purpose print method:

339-340 page program

Take a careful study:

340 page

Note that there is no information related to the sequence type. All of us have ENUMERATION. In order to solve the sequence, an enumeration is sufficient: can get the next object, and it is known whether it has arrived at the end. Get a series of objects, then traversal, thereby performing a specific operation - this is a very valuable programming concept, and this book will take this idea in many places. This seemingly special example can even be more common because it uses a regular TSTRING () method (called normal, because it belongs to a part of the Object class). Here is another method that calls printing (although it may be different in efficiency):

System.out.println ("" E.NEXTELEMENT ());

It uses "automatic conversion into string" technology encapsulated to Java. Once the compiler encounters a string, follow one " ", you will want to follow and follow one string and automatically call toString (). In Java 1.1, the first string is unnecessary; all objects convert into strings. It can also be performed on this, and the same effect is obtained with the same effect as calling toString:

System.out.println (String) E.NEXTELEMENT ())

But what we want to do is usually not just call the Object method, so it will face the problem of type modeling. For types of interest, you must assume that you have obtained an enumeration, then make the results object shape into that type (if the operation error will get the runtime violation).

8.4 Collection Type

Standard Java 1.0 and 1.1 library support offer a very small series of sets. But for most programming requirements, they are basically competent. As everyone will see at the end of this chapter, Java 1.2 provides a set of redesigned large collection libraries.

8.4.1 VECTOR

The use of Vector is simple, which has been proof in the previous example. Although we most only need to insert an object with addelement (), use Elementat () to extract an object with Elements () to obtain a "enumeration" of the sequence. But there are still other series of methods very useful. Like us, we don't use or describe all of these methods here for Java libraries. But please read the corresponding electronic documentation, there is a probably aware of their work.

Crash Java

The Java standard collection contains the toString () method, so they can generate their own String expressions, including the objects they accommodate. For example, in the vector, TString () will step and traversal in the various elements of the vector, and call toString for each element. Assume that we now want to print your own address. It seems that it seems to be simply reference this (especially the C programmer has the tendency to do):

341 page program

If you just simply create a CrashJava object and print it out, you will get an endless range of violation errors. However, if the CrashJava object is placed in a vector, and if you print the vector as you demonstrate, there will be no error prompts, even a violation will not appear. At this point, Java simply crashes (but at least it does not collapse my operating system). This has been tested in Java 1.1.

At this time, the automatic type conversion of the string occurs. When we use the following statement:

Crashjava Address: " this

The compiler found a " " after a string and other things that seem to be not strings, so it will try to convert this into a string. Calling to TOSTRING () when converting is toString (), which generates a recursive call. If this kind of thing appears in a vector, it seems that the stack will overflow, and the violation control mechanism does not respond at all. If you really want to print the address of the object in this case, the solution is to call the Object's TOString method. You don't have to join this at this time, just use super.toString (). Of course, there is also a premise that this approach: We must inherit from Object, or there is no parent class overwrites the Tostring method.

8.4.2 BitSet

BitSet is actually a vector consisting of "binary bit". If you want to save a lot of "On-Off" information efficiently, you should use BitSet. It only makes sense only from the point of view of size; if you want high efficiency access, then its speed is slower than using some inherent types of arrays.

In addition, the smallest length of BitSet is a long integer (long) length: 64 bits. This means that if we are ready to save smaller data, such as 8 digits, then bitset is wasting. So it is best to create your own class and use it to accommodate your marker.

In an ordinary vector, add more and more elements, the collection will also expand themselves. To a certain extent, BitSet is no exception. That is, it sometimes expands themselves, sometimes it is. And the 1.0 version of Java seems to be the worst in this regard, and its BitSet is very uncommon (Java1.1 has corrected this issue). The following example shows how BitSet works, and the error is demonstrated:

342-344 page program

Random digital generator is used to create a random Byte, Short, and Int. Each will be converted into a corresponding bit model in the bitset. Everything is normal, because BitSet is 64-bit, so they do not cause the final size to increase. But in Java 1.0, once BitSet is greater than 64, there will be some confusing behavior. If we set a bit that is only 1 bit from BitSet, it can be expanded normally. But once an attempt is set in a higher position, it will get an annoying violation without first contacting the boundary. This is exactly because BitSet cannot be extensified in Java 1.0. This example creates a 512-bit BitSet. The storage space allocated by the builder is twice the number of bits. So if you set a bit 1024 or higher, you don't set the bit 1023 first, you will get a violation in Java 1.0. But fortunately, this problem has been corrected in Java 1.1. So if you write code for Java 1.0, try to avoid using BitSet.

8.4.3 Stack

Stack is sometimes referred to as "post-first out" (LIFO) collection. In other words, we will last "Press" in the stack will be the first "pop-up" in the future. Like all other Java collections, we are pressing and popping out of "object", so you must "shape" on something that you populate.

A rare practice is to refuse to use the product as a basic constituent element of a Stack, but "inherit" a Stack from the vector. In this way, it has all the characteristics and behaviors of a Vector, plus some additional Stack behavior. It is difficult to judge that the designer is clearly trying to do so, or it is a natural design.

Below is a simple stack example, which can read each line of the array while pressing it as a string into the stack.

345 Program

Each line of the MONTHS array is inherited into the stack by PUSH (), which is removed from the top of the stack () later. The point to be declared is that Vector operations can also be done for the Stack object. This may be a vector, which is determined by the inheritance - Stack "belongs to". Therefore, the operations that can be performed on the VECTOR can also be performed for STACK, such as the Elementat () method. 8.4.4 Hashtable

Vector allows us to make a selection from a series of objects, so it actually associates numbers. But if we want to choose a series of objects according to other criteria? The stack is an example of this: its selection criteria is "Things to press the stack". This "Select from a series of objects" can also be called a "mapping", "dictionary" or "associated array". From the concept, it looks like a vector, but it is not to find objects through numbers, but use another object to find them! This usually belongs to an important process in a program.

In Java, this concept is specifically reflected on abstract Dictionary. The interface of this class is very intuitive () tells us how many elements are included; ISEMPTY () determines if an element (TRUE) is included; put (Object Key, Object Value) Add a value (what we hope ), Associate it with the same key (want to search for it); Get (Object Key) Gets the value corresponding to a key; and remove (Object Key) is used to delete "keys - value from the list" "Correct. You can also use enumeration techniques: keys () generate an enumeration (ENUMENTS () generates an enumeration to all values. This is all of the Dictionary (Dictionary).

Dictionary's implementation process is not trouble. A simple method is listed below, which uses two vectors, one for housing keys, and the other to accommodate the value:

346-347 page program

In the definition of Assocarray, the first question we noticed is its "extension" dictionary. This means that Assocarray is a type of Dictionary, so you can send it as a request as Dictionary. If you want to generate your own Dictionary, and just do it here, all things to do are only filled all methods located in Dictionary (and must overwrite all methods because they - except for builders - abstract).

Vector Key and Value links through a standard index number. That is to say, if Put () is called with a value of "ROOF" and "Blue", it is assumed that we are ready to associate with their paint colors, and there have been 100 elements in Assocarray. So "ROOF" will have 101 key elements, and "Blue" has 101 value elements. And pay attention to get (), if we pass "ROOF as the key, it produces index numbers with keys.index.of (), then generates the value in the associated value vector with that index number.

The tests in main () are very simple; it just converts your lowercase characters to uppercase characters, which is clearly available in a more effective way. But it reveals the powerful features of Assocarray.

The standard Java library contains only a variant of Dictionary, named HashTable, annotated 3). Java's hash table has the same interface as Assocarray (because both inherit from Dictionary). However, one aspect reflects the difference: perform efficiency. If you think about something you must do, you will find that the speed of search keys in a vector is much slower. But at this time, use a hash table can accelerate a lot of speed. It is not necessary to use a lengthy linear search technology to find a key, but use a special value, named "Surgia". The hash code can get information in the object, then convert it to the object "relatively unique" integer (Int). All objects have a hash code, and have HashCode () is a method of root Object. HashTable Gets HashCode () of the object, then use it to quickly find the key. This allows performance to be greatly increased (4). The specific working principle of the hash table has exceeded the scope of this book (5) - everyone only needs to know that the hash table is a fast "Dictionary", and the dictionary is a very useful tool. 3: If you plan to use RMI (detail in Chapter 15), you should pay attention to a problem when you place a remote object to have a loose list (see "Core Java", author conrell and Horstmann, Prentice-Hall 1997 publication)

4: If this speed is improved, you still can't meet your requirements for performance, and you can even write your own hash routine, thereby further accelerating the search process of the table. This avoids the time delay in the shape between the Object, or the synchronization process built by the Java class library routine can also be avoided.

5: My best reference book is "Practical Algorithms for Programmers", the author is published for Andrew Binstock and John Rex, Addison-Wesley 1995.

As an example of the application hash table, consider using a program to verify the randomness of Java's math.random () method. Ideally, it should produce a series of perfect random distribution numbers. But in order to verify this, we need to generate a large number of random numbers and then calculate how much falling within a different range. The hash table can greatly simplify this job because it enables objects to be associated with objects (at this time, the value generated by math.random () is associated with the number of values.). As follows:

348-349 page program

In Main (), a random number is generated each time, which will be encapsulated in an Integer object, allowing the handle to use with the hash table (cannot use the basic data type to use a collection, only the object handle). ContainKey () method Checks if this button is already in the collection (that is, is the number you found before?) If you have already in the collection, the get () method gets the value associated with the key, this is a Counter (counter) Object. The value I in the counter will then increase 1, indicating that this particular random number has appeared again.

If the key has not been found before, the method PUT () will still be placed in the hash table into a new "key-value" pair. At the beginning of the creation, Counter will automatically initialize its own variable i, which marks the first appearance of the random number.

To display the hash table, simply print it simply. HashTable toString () method can traverse all keys - value pairs and call toString for each pair. Integer toString () is a predetermined definition, you can see the TString used by the counter. The result of a run (add some of the wraps) as follows: 350 page

Everyone maybe if it is necessary to feel puzzled with the Counter class, it seems that there is no functionality of encapsulated integer. Why not use int or integer? In fact, since all the object handles can be accommodated, the integer can not be used at all. After you have learned a collection, the concept of package category may be more likely to understand, because any basic data type is not placed in a collection. However, the only thing we can do for the Java package is to initialize it into a specific value and then read the value. That is, once the encapsulator object has been created, there is no way to change a value. This makes the INTEGER package disseminated our problem, so you have to create a new class, use it to meet your own requirements.

1. Create a "key" class

In the previous example, we use a standard library class (Integer) as a button of HashTable. As a key, it works well because it already has all the conditions that run correctly. But when using the hash table, once we create your own class as a key, you will encounter a very common problem. For example, assume that a set of weather forecasting systems match the Groundhog (soil rat) object into a PREDiction. This looks very intuitive: We create two classes, then use the Groundhog as a key, and use the prediction as a value. As follows:

350-351 page program

Each Groundhog has an identification number, so it's a prediction in the aquary list, just indicate it "tell me the prediction with the Groundhog number 3". The Prediction class contains a Boolean value that is initialized with Math.random (), and a toString () interprets our results. In Main (), a hash table is populated with GROUNDHOG and Prediction related to them. The hash table is printed so that we can see that they have been filled. Subsequently, a forecast corresponding to GROUNDHOG # 3 is found with the identity number 3.

It seems that it seems very simple, but it is actually not feasible. The problem is that Groundhog is inherited from a universal Object root class (if the base class is not specified, all classes are ultimately inherited from Object). In fact, use Object's HashCode () method to generate the hash code for each object, and by default only the address of its objects. Therefore, the first instance of Groundhog (3) does not generate a hash code equal to the second instance of the Groundhog (3), and we retrieve it with the second instance.

Everyone may think that all things to do at this time is to correctly cover Hashcode (). But doing so, you can't do this, unless another thing: override is also part of the Object (). This method is used when the hash table attempts to determine if our keys are equal to a key in the table. Similarly, the default Object.Equals () simply compares the object address, so a GROUNDHOG (3) is not equal to another GROUNDHOG (3).

Therefore, in order to use its own class as a key in the hash table, it must be covered with HashCode () and equals (), as shown below: 352 Page

Note that this code uses the prediction from the previous example, so SpringDetector.java must be compiled first, otherwise it will get a compile period error when trying to compile SpringDetector2.java.

Groundhog2.hashcode () returns the soil rat number as an identifier (in this example, the programmer needs to guarantee that there is no two soil mouse to coexist with the same ID number). In order to return a unique identifier, there is no need for hashcode (), and the equals () method must be able to strictly determine whether the two objects are equal.

Equals () method To perform two check: Check if the object is null; if not null, continue checking whether it is a instance of GROUNDHOG2 (you want to use the instanceof keyword, Chapter 11 will be discussed in detail). Even in order to continue Equals (), it should also be a GROUNDHOG2. As everyone saw, this comparison is based on the actual Ghnumber. Once we run the program, it will see that it has finally produced the correct output (many Java libraries covers the HashCode () and Equals () methods to adapt to the content provided by themselves).

2. Properties: a type of HashTable

In the first example of this book, we used a HashTable type called Properties. In that example, the following procedures:

Properties P = system.getproperties ();

p.List (System.out);

A Static method called getProperties () is called for obtaining a special Properties object to describe certain features of the system. List () belongs to a method of Properties to send content to any of the stream outputs you selected. There is also a Save () method that can be used to write the property list to a file for later use of the LOAD () method.

Although the Properties class is inherited from HashTable, it also contains a list of "default" attributes. Therefore, if you don't find an attribute in the primary list, you will automatically search the default properties.

The Properties class can also be used in our program (ClassScanner.java in Chapter 17). In the user documentation of the Java library, more, more detailed descriptions can be found.

8.4.5 Reopemony

We can now demonstrate the true power of ENUMERATION: Separate the operation of the sequence to the infrastructure of that sequence. In the following example, the PrintData class is moved in a sequence with an enumeration and call the toString () method for each object. At this point, two different types of collection: a vector and a HashTable. These classes are filled in MOMSTER objects in them (this chapter has defined these classes earlier; "Note You must compile HAMSTERMAZE.JAVA and WORKSANYWAY.JAVA, otherwise the following programs cannot be compiled). Since Enumeration hides the structure of the base collection, PrintData does not know or not care about what type of ENUMERATION comes from:

354 program

Note PrintData.print () takes advantage of the objects in these collections belong to the object of the Object class, so it calls toString (). But when you solve your own practical problems, you often have to ensure your ENUMERATION crossing a particular type of collection. For example, all elements in the collection may be required to be a shape (geometric shape) and contain a Draw () method. If this happens, the Object returned from Enumeration.NexTelement () must be traced to create a shape. 8.5 Sort

The same thing in Java 1.0 and 1.1 libraries is the arithmetic operation, and there is no simpler sorting method. Therefore, we'd better create a vector, sort it itself with a classic Quicksort method.

When writing generic sorting code, a problem facing must perform comparison operations based on the actual type of object, thereby achieving correct sorting. Of course, a way is to write a different sorting method for each different type. However, it should be recognized that if this is done, it is not easy to realize the reuse of the code when adding a new type.

The program design is a major goal is to "there will be changed things that remain unchanged." Here, the unchanged code is a universal sort algorithm, and each time it is used, it is the actual comparison method of the object. Therefore, we cannot "hardcode" to multiple different sort routines, but use the "callback" technology. With callbacks, the part of the code that often changes will be encapsulated into its own class, but always maintain the same code, "callback" changes. In this way, different objects can express different comparison methods while passing the same sort code to them.

The "Interface" below shows how to compare two objects, which put those "things that have changed" are encapsulated:

355 page program

For these two methods, LHS represents the "left hand" object in this comparison, while RHS represents "right hand" object.

You can create a subclass of the Vector and implement "quick sort" via Compare. For this algorithm, including its speed and principle, etc., not specifically described herein. For more information, refer to BinStock and Rex "Practical Algorithms for Programmers", published by Addison-Wesley in 1995.

Page 355-356

Now, everyone can understand the origin of the word "callback", which is because the Quicksort () method "Tune" is a method in Compare. It is also understandable how this technique generates common, reusable (regenerated) code.

To use SortVector, you must create a class to implement Compare for our preparation. At this time, the internal class is not particularly important, but the organization of the code is beneficial. Below is an example of a String object:

356-357 page program

The internal class is "static" because it does not need to connect an external class.

Everyone can see that once the framework is set, it can be very convenient to repeat one of the images like this - just simply writing a class, put "need to change", then pass an object to SortVector I.e.

When compared, the strings are enforced as lowercase, so uppercase A will be arranged next to lowercase A, without moving a completely different place. However, this example also shows a deficiencies of this method because the above test code is arranged in the order in order to arrange the same letter and lowercase form: A B B C C D D. But this is usually not a big problem, because often handling is a longer string, so the above effect will not reveal (the collection of Java 1.2 provides sorting function, which has solved this problem). Inheritance (Extends) is used here to create a new type of vector. That is, SortVector belongs to a vector and has some additional features. Inheriting is a big role here, but it has brought a problem. It makes some methods have the final property (which is already in Chapter 7), so you cannot overwrite them. If you want to create a sequenceful vector, you will have trouble only receive and generate a String object. Because addelement () and Elementat () have a final property, and they are all methods we have to overwrite, otherwise it is only possible to receive and generate String objects.

But on the other hand, consider adopting the "synthetic" method: put an object into the interior of a new class. At this point, it is not to rewrite the above code to achieve this, but simply use a SortVector in the new class. In this case, the internal class used to implement the COMPARE interface can be "anonymous". As follows:

358-359 page program

This can quickly regenerate the code from the SortVector, thereby gaining the desired function. However, all public methods from SortVector and Vector can appear in STRSORTVECTOR. If you regenerate the code in this form, you can generate a definition for each of the new classes to contain each way. Of course, you can also add only a few minutes when you just start, and then add more as needed. The design of the new class will eventually stabilize.

The advantage of this method is that it still only accepts the String object, and only the String object is generated. Moreover, the corresponding check is done during compilation, not at the runtime. Of course, only addelement () and Elementat () have this feature; Elements () still produces an enumeration, which is undecided in the compile period. Of course, for Enumeration and type in STRSORTVector, check it out; if there is anything wrong, it will simply generate a violation during operation. In fact, can we ensure that everything is correct during compiling or running? (That is, "the code test may not be guaranteed", and "the user is likely to do some things that have not been tested"). Despite other options and debates, use inheritance is much easier, but people are deeply inconvenient during shape. Similarly, once the parameterization type is added to Java, it is expected to solve this problem.

Everyone can see a sign named "sorted" in this class. Each time you call AddElement (), you can sort the vector, and it is continuously held in a state of a row. But before starting reading, people always add a lot of elements to a vector. So in each of the addelement (), it is better to wait until someone wants to read the vector, and then sort it. The latter is much higher. This unless absolutely necessary, it is called "lazy evaluation" (there is a similar technique called "lazy initialization" - unless I really need a field value, it is not initialized). 8.6 General Turning Library

Through this chapter, everyone knows that the standard Java library provides some particularly useful collections, but the collection of the full meaning is far away. In addition, the algorithm sorted is not supported at all. One place in C is its library, especially the "Standard Template" (STL) provides a set of relatively complete sets, as well as many algorithms such as sorting and retrieving such a set of aggregations. I feel this status quo, based on this model, ObjectSpace has designed the "General Collection Library" of the Java version (which is called "Java Universal Library", JGL; but JGL's abbreviation infringes the copyright of Sun Company - - Although this book still uses this briefly. This library is as designed with STL as much as possible (taken care of the difference between two languages). JGL achieves many functions to meet most of the general needs of a collection library, which is very similar to the C template mechanism. JGL includes lists, settings, queues, mappings, stack, sequences, and repetitions, and repetitions, which are more than enumeration. At the same time, a complete algorithm is provided, such as retrieval and sorting. In some ways, ObjectSpace design is also "smart" than Sun's library scheme. For example, the method in the JGL collection will not enter the final state, so it is easy to inherit and rewrite those methods.

JGL has included a Java kit from some vendors, and ObjectSpace also allows all users to use JGL for free, including commercial use. Details and software downloads can be accessed http://www.objectspace.com. The online documentation provided with JGL support is very good and can be used as an excellent starting point.

8.7 new collection

For me, the collection class belongs to the most powerful tool, especially suitable for use in original programming. Everyone may have feeled that I am a little disappointed with Java 1.1. Therefore, it is indeed very enjoyable to see Java 1.2 reorganized the collection. The collection of this version has also been completely redesigned (by Sun's Joshua Bloch). I think the collection of new designs is one of the two most important features in Java 1.2 (the other is the Swing library, which will be described in Chapter 13), because they greatly facilitate our programming, but also make Java into one More mature programming system.

Some design makes the combination between elements becomes more tight, and it is more likely to be understood. For example, many names become shorter, more clear, and more easy to use; the type is the same. Some names have been modified, closer to popular: I feel that a particularly good one is to replace "Enumeration" with "inlance".

The redesign has also enhanced the function of the collection library. Now new behaviors include link lists, queues, and undo teams (ie, "Double Tast Queue").

The design of the collector is quite difficult (will encounter a large number of design issues). In C , STL covers the foundation with multiple different classes. This approach is a big progress than STL, which didn't do this at all. But still don't switch to Java very well. The result is a class that is particularly easily confused. On the other extreme, I have found a collection of sets consisting of a single class: Colleciton, which is used as a Vector and HashTable. The designer of the new collection library wants to reach a new balance: achieving the full functionality you want to get from a mature collection library, while more easily learning and use more than STL and other similar set libraries. The result of this is quirky in some occasions. However, some decisions in the early Java library have different decisions, these weirds are not accidental, but the result of complexity as the cost, after careful weighing. This may extend people to master some library concepts, but will soon find themselves to use those new tools, and become more and more can't alive. The new set library takes into account the "accommodation of their own objects", and divides them into two clear concepts:

(1) Collection: a set of separate elements, usually applied some rules. Here, a list must be accommodated in a specific order, while a set (set) cannot contain any repetitive elements. Instead, the concept of "package" (BAG) is not implemented in a new set library, because "List" has provided similar features.

(2) Mapp: A series of "key-value" pairs (this has been fully reflected in the hash table). From the surface, this seems to be a "collection" pair of "key-value", but if you try to implement it in that way, it will find that the implementation process is quite awkward. This further proves that it should be separated into a separate concept. On the other hand, you can easily view a portion of the map. Just create a collection and use it to represent that part. In this way, MAP can return a set of a set of its own key, a list containing its own value or contains the "key-value" pair. Similar to the array, MAP can easily expand into multiple "dimension", no need to involve any new concept. Simply contain other MAP in a map (the latter can also contain more MAP, and push).

Collection and MAP can be implemented in a variety of forms, which is determined by programming requirements. The following is a new collection schematic of a helpful understanding:

363 pages

When this picture was just beginning, it couldn't touch the mind, but after reading this chapter, I believe that everyone will truly understand it actually only three collection components: Map, List, and SET. And each component is actually only two, three implementations (annotation 6), and usually only has a particularly good way. As long as you see this, the collection will not be a fear.

6: When writing this chapter, Java 1.2 is still in the beta test phase, so this schematic diagram does not include the Treeset that will be added later.

The dotted box represents "Interface", the point line box represents the "Abstract" class, and the solid line box represents the normal (actual) class. The point line arrow indicates a specific class ready to implement an interface (in the abstract class, "section" implements an interface). Double-line arrows represent objects of the class that can generate the arrow pointing to. For example, any collection can generate a repetition (Iterator), and a list can generate a listiterator (and the original repetition because the list is inherited from the collection). The interface dedicated to accommodating the object is Collection, List, SET, and MAP. In the traditional situation, we need to write a lot of code to deal with these interfaces. And in order to specify the exact type you want to use, you must set it at the beginning of the creation. So it is possible to create a list below:

List x = new linkedlist ();

Of course, you can also decide to use X as a LinkedList (rather than a normal list), and use X load accurate type information. The advantage of using the interface is that once it decides to change its implementation details, all things to do is changed it when you create it, just like this:

List x = new arraylist ();

The rest of the code can be maintained.

In the classified structure of the class, you can see a large number of classes starting with "abstract" (abstract), which is just that people can feel confused. They are actually some tools for "part" to implement a specific interface. For example, if you want to generate your own SET, you will not start from the SET interface, and then implement all the methods yourself. Instead, we can inherit from AbstractSet, just get our new classes very little. Despite this, the new collection library still contains enough features to meet the almost all needs of us. So taking into account our goal, you can ignore all classes starting with "abstract".

Therefore, when viewing this schematic, it is really necessary to care about "interface" and ordinary (actual) class - all enclosed in a solid line box. An object of the actual class is usually required, and it is traced into the corresponding interface. The interface can be used anywhere in the code. Here is a simple example, which populates a collection with the String object, then print each element in the collection:

364-365 page program

All code examples of the new collection library are placed under the subdirectories NewCollections, which reminds themselves only valid for Java 1.2. In this way, we must call the program with the following code:

Java c08.newcollections.simplecollection

The syntax used is similar to other programs.

You can see a part of the new collection belong to a part of the Java.util library, so you don't need to add any additional import statements when you use.

The first row of Main () creates an ArrayList object and then it becomes a collection. Since this example only uses a Collection method, any object from a class inherited from Collection can work. But ArrayList is a typical Collection that replaces the location of the Vector.

Obviously, the role of the add () method is to place a new element into a collection. However, user documentation is cautiously pointing out that add () "guarantees that this collection contains the specified element." This is a SET is paved, and the latter only adds that element only under the premise of the elements. For ArrayList and any other form of list, add () must certainly mean "directly add".

With the item () method, all collections can generate a "negative" (Iterator). The repetition is actually like a "enumeration", an alternative to the latter, just: (1) it adopts a history default, and it has been widely adopted in OOP (repetition).

(2) The name is shorter than the enumeration: HasNext () instead of HasmoreElement (), and next () replaces nextElement ().

(3) Add a new method called Remove () to delete the previous elements generated by Iterator. So when you call next (), just call REMOVE () once.

In SimpleCollection.java, you can see a repetition and use it in the collection, print each element.

8.7.1 Using Collectes

The following table summarizes all things that can be done with a collection (also doing the same thing to SET and LIST, although List also provides additional features). Map is not inherited from Collection, so you have to treat it separately.

366-367 page table

Boolean Add (Object) * Ensure that the argument is included in the collection. If it does not add an argument, return false (fake)

Boolean Add (Collection) * Add all elements in the argument. Return true if you do not add an element (true)

Void clear () * Delete all elements in the collection

Boolean Contains (Object) Returns "true" if the collection contains an argument

Boolean ContainSall (Collection) Returns "true" if the collection contains all the elements in the own variable

Boolean ISempty () If there is no element in the collection, it returns "true"

Iterator iterator () returns a repetition to use it to traverse the elements of the collection

Boolean Remove (Object) * If the argument is in a collection, an instance of that element is deleted. If you have already deleted, return "true"

Boolean Removeall (Collection) * Delete all elements in the argument. If you have already deleted any delete, return "true"

Boolean RetainAll (Collection) * only retains elements contained in a self-variable (a theoretical "intersection"). If you have changed any changes, it will return "true"

INT size () returns the number of elements within the collection

Object [] toArray () returns an array containing all elements in the collection

* This is an "optional" method, and some collection may not implement it. If this is true, the method will encounter a unsupportedoperatiionException, which is a "operation does not support" violation, see Chapter 9.

The following example demonstrates all methods. Similarly, they are only valid for things inherited from the collection, an arraylist uses as a "uncommonly used denominator":

367-369 page program

With the first method, we can use test data to populate any collection. In this case, only INT is converted into string. The second method will be used frequently in the rest of this chapter.

Two versions of NewCollection () have created ArrayList for returning them as a collection object as a collection object. Therefore, it is obvious that in addition to the Collection interface, we will not use anything else. The Print () method will also be used in this section. Since it uses a repetition (Iterator) in a collection, any collection can produce such a repetition, so it applies to List and SET, which also applies to Collection generated by a MAP.

Main () shows all methods in the collection with a simple means.

In subsequent sections, we will compare different implementations of List, SET, and MAP, and it is pointed out which solution should be preferred (with an asterisk) in various situations. Everyone will find that some traditional classes are not included, such as Vector, Stack, and Hashtable. Because in any case, there is a class of choice in the new collection.

8.7.2 Using Lists

369-370 pages

The order of the List is the most important feature of List; it guarantees elements to arrange in the predetermined order. List adds a large number of methods to Collection so that we can insert and delete elements in the middle of the List (recommended for LinkedList). List also generates a listiterator (list repetition) that can be traversed in two directions in one list while inserting and deleting elements located in the middle of the list (the same, only suggesting to do this for LinkedList)

ArrayList * is pushed by an array. As a regular use of object containers, it is used to replace the original Vector. Allows us to quickly access the elements, but when inserting and deleting elements from the middle of the list, the speed is too slow. Generally, you should only use listiterator to forward and backward traversal, don't use it to delete and insert elements; compared with LinkedList, it is much more efficient.

LINKEDLIST provides optimized order access performance, while inserting and deleting operations in the middle of the list. However, when random access, the speed is quite slow, and ArrayList should be used at this time. AddFirst (), addlast (), receivefirst (), getlast (), transovefirst (), and removelast () (not defined in any interface or underlying class) so as to make it as a speccture, queue, and a two-way queue. use

The method in this example below covers a set of different behaviors: every list can do (BasicTest ()), through a repetitive traversal (Itermotion (), change certain things with a repetition (Itermanipulation ()), experience list processing (Testvisual ()) and only LinkedList can do things:

370-373 page program

In BasicTest () and itermotiion (), it simply invokes to reveal the correct syntax. And although the return value is captured, it is not used. In some cases, the reason why the return value is not captured is due to the special use of them. Before using them, you should carefully study your online documentation, master these methods intact, correct usage.

8.7.3 Using Sets

SET has exactly the same interface as Collection, so it is different from two different LISTs, it doesn't have any additional features. Instead, SET is completely a Collection, just having different behavior (this is the ideal application of examples and versatile: used to express different behavior). Here, a set only allows each object to exist an instance (as you will see later, the "value" of an object is quite complex). 374 pages

Each element added to the SET must be unique; otherwise set will not add a repeated element. The object added to the SET must define Equals () to establish the uniqueness of the object. SET has the same interface as Collection. A SET cannot guarantee your own elements in any particular order.

Hashset * is used in all SETs other than often small. Objects must also define havehcode ()

ArraySet is pushed by a array. Facing very small SET design, especially those that need to be created and deleted frequently. For small SETs, the cost of arrayset creation and repetition is much smaller than the HashSet. But with the increase of SET, its performance will also be greatly reduced. No need for Hashcode ()

The Treeset is subjected to the order of "red black tree" (notes 7). In this way, we can mention a sequential collection from a set.

7: When the book is written, Treeset is still only announced, and it has not been officially implemented. So there is no example of using Treeset here.

The following example does not list all things that can be made with a set, because the interface is the same as the collection, the precedent has been practiced. Instead, we must exist of a single set of acts:

374-375 page program

Duplicate values ​​are added to SET, but when printing, we will find that set only an instance of each value.

When running this program, it is noted that the order in which the Hashset maintained is different from arrayset. This is because they use different ways to save elements so they can be positioned later. ArraySet maintains their order state, while HashSet uses a hash function, which is especially designed for fast retrieval). When you create your own type, be sure to pay attention to the SET needs to maintain a storage order, just like this chapter, "GROUNDHOG" (soil rat) example of this chapter. Below is an example:

Page 375-376

The definition of equals () and hashcode () Follow the form of "GROUNDHOG" example already given. In both cases, you must define an equals (). But only if you want to put the class with a Hashset, it is necessary to use HashCode () - this is totally possible because it should usually be selected as a SET implementation first.

8.7.4 Using MAPS

376-377 page table

MAP (interface) maintains the "key-value" correspondence (right) to find the corresponding value via a key

HashMap * is implemented based on a hash table (with it instead of Hashtable). Insert and retrieve the "Key-value" pair, this form has the most stable performance. This performance can be adjusted by the builder to set the "capabilities" and "loading factors" of the hash table.

ArrayMap is pushed by an ArrayList. Accurate control is provided in the sequence of repeated. Facing very small MAP design, especially those that need to be created and deleted. For very small MAP, the cost payable for creating and repeating is much lower than HashMap. However, after MAP becomes large, performance will also reduce TreeMap on the basis of a "red-black" tree. When viewing the key or "Key-Value", they will be arranged in a fixed order (depending on the Comparable or Comparer, will be told later). The biggest advantage of TreeMap is that we have obtained the result of the sequence. TreeMap is the only MAP containing the Submap () method, using it to return to a part of the tree

The following example contains two sets of test data and a Fill () method that can be populated with any two-dimensional array (composed of Object) using this method. These tools are also used in other map examples.

377-379 page program

PrintKeys (), printvalues ​​(), and print () methods are not only useful tools, which also clearly reveals the process of generating a MAP Collection "Shot." KeySet () method will generate a set, which is pushed by the keys in the Map. Here, it is only treated as a Collection. Values ​​() also got similar trend, its role is to generate a list, which contains all values ​​in the map (note that the key must be unique, and the value can be repeated). Since these Collection is pushed by MAP, any changes in a collection will be reflected in the corresponding map.

The role of the Print () method is to collect the Iterator (repetition) generated by Entries, and print out the keys and values ​​of each "key-value". The remainder of the program provides a simple example of each MAP operation and tests each type of MAP.

When you create your own class, you must pay attention to the same problem as the previous SET when you use a key in your MAP.

8.7.5 Decision Implementation Plan

It can be seen from the schematic from the earlier, in fact, only three collection components: Map, List, and SET. And there are only two or three embodiments each interface. If you need to use the features provided by a specific interface, how can I decide which solution is taken?

To understand this problem, you must recognize that every different implementation has its own characteristics, advantages and disadvantages. For example, in that picture, you can see that "Features" of HashTable, Vector, and Stack is that they are all "traditional" classes, so they do not interfere with the original code. But on the other hand, try to use them for new (Java 1.2) code as much as possible.

Differences between other collections usually can be summarized as "post-push". In other words, it depends on what is the data structure used to implement the target interface in the physical sense. For example, ArrayList, LinkedList, and Vector (substantially equivalent to arraylist) implements a List interface, so our programs are similar results no matter which one is selected. However, ArrayList (and vector) is pushed by a array; LinkedList is implemented in a regular dual link list because each individual object contains data and a handle to the front and rear elements in the list. It is for this reason. If you want to make a lot of insertion and delete operation in a list, then LinkedList is undoubtedly the most appropriate choice (LINKEDLIST has some additional features, built in AbstractSequentialList). If you do this, you will be willing to choose ArrayList, which may be fast. As another example, SET can be implemented as an ArraySet, or as a Hashset implementation. ArraySet is pushed by an ArrayList, which is designed to support only a small amount of elements, especially suitable for applications where to create and delete a large SET object. However, once you need to accommodate a lot of elements in your own set, the performance of ArraySet will be greatly reduced. Write a program that requires a set, you should choose the Hashset by default. And only in certain special circumstances (an urgent need to improve performance) can only be switched to ArraySet.

1. What kind of List is decided to use

In order to experience the difference between the various List implementations, the easiest way is to conduct a performance test. The role of the following code is to create an internal base class that uses it as a test bed. Then create an anonymous internal class for each test. Each such internal class is called by a Test () method. With this method, you can easily add and remove test items.

380-382 page program

Internal Tester is an abstract class that provides a basic class for specific tests. It contains a string to be printed at the beginning of the test, a Size parameter for calculating the number of tests or elements, a builder for initializing the field and an abstract method Test (). Test () is the most practical test work. Various types of tests are concentrated in a place: Tests array. We initialize the array with different anonymous internal classes inherited in Tester. To add or delete a test item, just simply add or remove an internal class definition in the array, all other tasks are automatically performed.

First use the element to fill the TEST () List, and then the test in the Tests array. Since the test machine is different, the results will certainly be different. The purpose of this program is to reveal the relative performance comparison of different set types. Below is the result of a certain run:

Type Get Repeated Insert Delete

ArrayList 110 270 1920 4780

LinkedList 1870 7580 170 110

It can be seen that random access is performed in ArrayList (ie, get ()) and loop repetition is most popular; but for LinkedList is a small overhead. On the other hand, inserting and deleting an operation in the middle of the list is much more cost-effective than the ArrayList for LinkedList. Our best practice may be to choose an ArrayList as its default starting point. If the performance is reduced due to a large amount of insertion and deletion, it is not too late to replace the LinkedList.

2. What kind of SET decided to use

You can make a choice between ArraySet and Hashset, depending on the size of the SET (if you need a sequential list from one set, use treetet; annotate 8). The following test procedures will help everyone make this choice: 383-384 page program

8: Treeset is not a formal feature in this book writing, but in this example, it can be easily added to it.

Finally, only 500 elements for the Arrayset test, not 1000, because it is too slow.

Type Test Size Add Containered

384-385 page table

When add () and contains (), have a much better than ArraySet, and the performance is significant with the geolism. When writing a program, it is almost never used to use ArraySet.

3. What maps are used to decide?

When choosing a different MAP implementation, pay attention to the size of the MAP for performance is the greatest, and this test program clearly explains this:

385-387 page program

Since the size of the MAP is the most serious problem, the timing test of the program is divided in size (or capacity) of the MAP to obtain a convincing test result. Listed below (possibly different on your machine):

Type test size placement Repeated

387 page slightly

Even if the size is 10, the performance of ArrayMap is also more than HashMAP - except for repeated cycles. And when using MAP, repeated role is usually not important (GET () is usually the most spended place to spend most). TreeMap provides excellent PUT () and repeated time, but GET () has poor performance. But why do we still need to use TreeMap? In this way, we may not use it as a MAP, and as a way to create a sequence list. The essence of the tree is that it is always arranged, and it does not have to be specifically sorted (its sorting method will now be said). Once a TreeMap is filled, KeySet () can be called to obtain a set "scene" of the key. Then use toArray () to generate an array containing those keys. Subsequently, you can use the Static method array.binarysearch () to quickly find the contents of the array of sorts. Of course, this approach is only necessary when it is unacceptable to HashMap. Because the design of HashMap is to perform fast retrieval operations. Finally, when we use MAP, the primary choice should be HashMap. Other methods are required only in a very small number of cases.

Also, in the table above, another performance problem is not reflected. The following procedure is used to test the creative speed of different types of MAP:

387-388 page program

During writing this program, TreeMap is more fast than the other two types (but you should try it yourself because the new version may improve the performance of ArrayMap). Considering this reason, due to the excellent PUT () performance of the aforementioned TreeMap, if you need to create a large number of maps, and only if you need a large retrieval operation, then the best policy is: create and populate TreeMap; When the amount increases, it will convert important TreeMap to HashMap - Use the HashMap builder. Similarly, only after the fact that there is really a performance bottleneck, it should be careful to use these aspects - first, then speed up the speed as needed.

8.7.6 Unsupported operation

With Static (static) array arrays.tolist (), you may convert an array into list, as shown below: 388-389 Page Program

It can be seen from it that only part of the Collection and List interfaces are actually implemented. The remaining methods have led to a unpopular case, named UNSUPPORTEDOPERATIONException. In the next chapter, we will tell the details of the violation, but it is necessary to make a brief explanation here. The key here is "Collecting Interface", as well as other interfaces in the new collection library, which all contain "optional" methods. In the collection class that implements those interfaces, or is provided, or not to provide support for those methods. If an unsupported method is called, it will cause a unsupportedOperationException (the operation does not support violations), which indicates a programming error.

Everyone may feel weird, not to say "interface" and the largest "selling point" is what they promise to produce some meaningful behavior? The above violations destroy the promise - a part of its call not only does not generate meaningful behavior, but also aborts the operation of the program. In these cases, the so-called security guarantee of the type seems to be worthless! However, the situation is not as bad as imagined. With Collection, List, SET or MAP, the compiler still limits the method we can only call the interface, so it does still have some differences and SMALLTalk (in SmallTalk, you can call any method for any object, and only run the program When you know if these calls are feasible). In addition, most of the methods of collection act as an argument can only be read from all "Read" methods of data in that collection.

In this way, the system can avoid conflicts between interfaces during design. In other designs of the collection library, they will eventually get excessive interfaces, and they use them to describe every variation of the basic solution, so learning and mastery is very difficult. Sometimes, it is even difficult to capture all the special circumstances in the interface because people may design any new interfaces. However, Java's "not supported operation" method has reached an important design goal of the new collection library: easy to learn and use. However, in order to make this method truly effective, it needs to satisfy the following conditions:

(1) UnsupportedOperationException must belong to a "very" event. That is, for most classes, all operations should be feasible. Only in some special circumstances, two operations may not be supported. The new collection library meets this condition because the vast majority of the class -Araylist, LinkedList, Hashlist, and HashMap, and other collection schemes provide support for all operations. However, if you want to create a new collection, you don't want to provide meaningful definitions for all the methods in the collection interface, which makes it still with the existing library, which also provides a "back door" that can be utilized.

(2) If an operation is not supported, the unsupportedOperationException (unsupported operational violation) is very likely to appear during the implementation, and will not appear after the product has delivered to the customer. It is pointed out in a programming error - uses a class inappropriately. This cannot be determined by it. It can also be seen that the "test" characteristics of this solution can only find the most ideal way of working.

In the above example, Arrays.tolist () produces a list, which is pushed by a fixed length array. So the only support is those that do not change the length of the array. On the other hand, if a new interface is requested to express different kinds of behavior (may be called "FixedsizeList" - a list of fixed lengths, there is a greater risk of more complexity. In this way, when I tried to use the library, I will soon find that I don't know where you are. For those methods that use collection, list, set, or Map as parameters, their documentation should point out which optional methods must be implemented. For example, sorting requirements implementation set () and item.set () methods, but not include Add () and remove ().

8.7.7 Sort and search

Java 1.2 adds its own set of utilities to arrange and search for array or lists. These tools belong to "static" methods of two new categories. These two classes are Arrays for sorting and searching arrays, and Collectes for sorting and searching lists.

An array

The ArrayS class provides an overloaded sort () and binarysearch () () () () () (), which can also be used for String and Object. The following example shows how to sort and search one byte array (all all basic data types are similar) and a String array:

391-392 page program

The first part of the class contains a utility used to generate random string objects, which are available in a character array. RandString () returns a string of any length; readstrings () creates an array of random strings while giving the length of each string and the desired array size. Two print () methods simplified the display of the exemplary array. In Main (), random.nextBytes () uses randomly selected bytes (no corresponding Random methods for creating arrays for creating other basic data types). After obtaining an array, you can find that simply calls to be called in order to perform sort () or binarysearch (). There is also an important warning associated with binarysearch (): If you do not call Sort () before executing binarysearch (), unpredictable behavior occurs, and even an infinite loop.

Sorting String and Search is similar, but when running the program, we will notice an interesting phenomenon: sorting compliance is a dictionary order, that is, uppercase letters are located in front of lowercase letters in the character. Therefore, all uppercase letters are located in the forefront of the list, followed by lowercase letters - Z actually in front of A. It seems that the phone book is also sorted.

2. Comparison and comparator

But if we don't satisfy this sorting method, what should I deal with? For example, the index behind this book is necessary to view both the entries starting with A or A, then it will definitely make the reader impatient.

If you want to sort an Object array, you must solve a problem. What is the order of two Object? Unfortunately, the initial Java designer does not think this is an important issue, otherwise it has already defined in the root Object. One consequence of this is that Object sorting must be performed from the outside, and the new set library provides standard way to implement this operation (the most ideal is to define it in Object).

For the Object array (and string, it is one of the Object), one sort () can be used, and it accepts another parameter: implements the Comparator interface (ie "comparator" interface, part of the new collection library) An object and compare it with its single compare () method. This method uses two preparative comparisons as its own parameters - if the first parameter is less than the second, return a negative integer; if the equal, return zero; if the first parameter is greater than the second, return forward Integer. Based on this rule, the String section of the above example can be rereadable, so that it is truly alphabetical sequence: 394 page

By styling String, the Compare () method will conduct a "hint" test to ensure that it can only be a String object - the runtime system will capture any errors. After combining two strings, the string.comPareto () method generates the expected result.

If you have your own Comparator, sort (), then you must use the same Comparer when using binarysearch ().

The Arrays class provides another sort () method that uses a single argument: an Object array, but there is no Comparator. This sort () method must also compare two Object in the same way. By implementing the Comparable interface, it adopts a "natural comparative method" that gives a class. This interface contains a single method - Compareto (), which can return negative numbers, zero or positive numbers according to it, equal to or greater than the argument, thereby implementing the object. This example is simply illustrated in this:

394-395 page program

Of course, our CompareTo () method can also increase complexity according to the actual situation.

3. List

A list (list) can be sorted and searched in the same form as an array. The static method for sorting and searching lists is included in class collections, but they have a signature similar to Arrays: sort (list) is used to sort a list of objects that implements Comparable; binarysearch (list, object) is used for Find an object in the list; Sort (List, Comparer) is sorted by a "comparator"; BinarySearch (List, Object, Comparer) is used to find an object in that list (comment ⑨). The following example takes advantage of pre-defined compclass and alphacomp to demonstrate all kinds of sorting tools in Collectes:

396 page

⑨: When writing in this book, a new collections.stables Out () has been announced, and it can be used in combination, but there is no test version of the beta.

The usage of these methods is exactly consistent with the usage in Arrays, just replacing an array with a list.

TreeMap must also sort your objects based on Comparable or Comparator.

8.7.8 utility

The Collectes class contains a large useful utility:

397 page

ENUMERATION (Collection) Enumeration for the origin of the array of origin (enumeration)

Max (Collection), MIN (Collection) produces the largest or minimum element in the natural comparative method of the target in the argument

Max (Collection, Comparection), MIN (Collection, Compare) generates the maximum or minimum element in a collection comparator

NCOPIES (INT N, OBJECT O) Returns a non-variable list of length n, all of which point to OSUBLIST (List, int min, int max), which is returned by the specified parameter list. You can imagine this list as a "window", which starts from the index for min, just ending the max of Max

Note that min () and max () work with the Collection object, rather than List, so don't worry if Collection needs to be sorted (like you pointed out, executing a binary search () - ie binary search - before Sort ()) must be performed on one List or a array.

1. Make Collection or MAP cannot be modified

Typically, a "read-only" version of Creating Collection or Map is more favorable. The Collections class allows us to achieve this goal, and the method is to pass the original container into a method and make it back to a read-only version. This method has four different forms of variations, which are used for Collection (if you don't want to treat a collection as a more special type), List, SET, and MAP. The following example demonstrates the correct way to build a read-only version respectively:

397-398 page program

For each case, the container must be filled with a valid data before it is formally become read-only. Once the load is successful, the best approach is to replace the existing handle with the handle generated by the "invisible" call. This can effectively avoid it to change the contents that will be cautious after unremnivation. On the other hand, the tool also allows us to keep the container that can be modified in a class, and can return a read-only handle that points to the container from a method call. In this way, although we can modify it in the class, others can only read.

Calling "Invised" methods for a particular type does not cause inspections during compilation, but once any change occurs, the call to modify the method of modifying a particular container will generate a unsupportedOperationException violation.

2. Synchronization of Collection or MAP

The synchronized keyword is a very important part of the "multi-thread" mechanism. We will discuss this mechanism in Chapter 14. Here, everyone only needs to note that the Collectes class provides a way to automatically synchronize the entire container. Its syntax is similar to the "unmodified" method:

399 page

In this case, we pass the appropriate "synchronous" method directly to the new container; doing this avoids unaptible version.

The new collection also provides mechanisms that prevent multiple processes simultaneously modify the content of a container. If you repeat in a container, you can intervene, delete or modify an object in that container, will face a risk of conflicts. We may have passed that object, maybe it is in front of us, and the size of the container has happened to shrink after we call Size () - we face a variety of possible dangers. In response to this problem, the new set library integrates a set of solutions that can detect any other modifications to the container except for our process you need to be responsible. If there are other aspects, it is also prepared to modify the container, it will immediately generate a ConcURRentModificationException (concurrently modified violation). We refer to this mechanism as "immediate failure" - it does not have to detect problems in "later", but "immediately" produce violations.

8.8 Summary

The following review provides a collection provided by the standard Java (1.0 and 1.1) libraries (BitSet is not included here because it is more like a class with special mission):

(1) The array contains the digitized index of the object. It is a known type of object, so when it looks for an object, it is not necessary to make the result to shape the results. The array can be multi-dimensional, and can accommodate the basic data type. However, once it creates it, the size cannot be changed. (2) Vector (vector) also contains the digital index of the object - the array and vector imagine a random access collection. When we add more elements, the Vector can automatically change its own size. However, the vector can only accommodate the handle of the object, so it does not include the basic data type; and when an object handle is taken from the collection, the results must be processed.

(3) Hashtable is a type of Dictionary (Dictionary), is a way to associate objects (rather than numbers) together with other objects. The hash table also supports random access to the object, in fact, its entire design is "high speed" that highlights.

(4) STACK is a queue of "Liu in First" (LIFO).

If you have been familiar with the data structure, you may have doubtable why didn't you see a bigger collection. From the perspective of the function, do you really need a bigger collection? For HashTable, you can place anything in it and retrieve very fast; for Enumeration, a sequence can be traversed, and each element is taken a specific operation. That is a powerful tool.

But hashtable has no "order" concept. Vector and arrands provide us with a linear order, but to insert an element into the middle of them, it is generally paid for the price of "heavy". In addition, queues, disassemble queues, priority queues, and trees involve "sorting" of elements - not only placed them in order to locate or move them in linear sequential sequential. These data structures are also very useful, which is why they contain them in standard C . Considering this reason, you should only see a collection of standard Java libraries as a starting point. And if Java 1.0 or 1.1 must be used, you can use JGL when you need to surpass them.

If you can use Java 1.2, you can only use the new collection, it can generally meet all our needs. Note This book has spent a lot of space in Java 1.1, so a lot of collections used in books can only be used in Java 1.1: Vector and Hashtable. As far as it is, this is an unhappy practice. However, such processing can also provide backward compatibility with old Java code. To write new code with Java1.2, new collections can often be better for you.

8.9 practice

(1) Creating a new class named gerbil, initializing an int GerbilNumber (similar to the Mouse example of this chapter). To write a method called hop (), use it to print out the number of GERBIL that meets the hop () condition. Built a vector and add a series of Gerbil objects for the Vector. Now, use the Elementat () method to traverse in the vector and call Hop ().

(2) Modifying Exercise 1, using Enumeration to traverse the vector while calling hop ().

(3) In Assocarray.java, modify this example, use a HashTable instead of Assocarray.

(4) Get the gerbil class used by the practice 1, change it into a HashTable, then use the Gerbil's name as a string (key) and the Gerbil placed in the table. Get a enumeration for keys (), and use it in the HashTable in the HashTable, find the Gerbil of each key, print the key, and then tell Gerbil to hop (). (5) Modifying the practice of Chapter 7 1, with a vector to accommodate RODENT (rodent) and traverse it in the RODENT sequence with Enumeration. Remember that the vector can only accommodate the object, so a model (such as RTTI) must be used when accessing a separate RODENT.

(6) Go to the intermediate position of Chapter 7, find the GreenhouseControls.java (greenhouse control) example, the case should be constructed of three files. In Controller.java, class EventSet is only a collection. Modify its code and use a Stack instead of EventSet. Of course, it may not only use Stack to replace EventSet simple; you also need to use an enumeration traversal event set. Considering that the collection is treated as a Stack at some time, while others treating it as a vector - thus makes things easier.

(7) (There is a challenge) to find the source code for the Vector in the Java source code library provided with all Java release packages. Copy these code, make a special version named intVector, which contains INT data. Thinking of whether you can make a special version of the Vector of all basic data types. Next, considering if a link list class is made, what happens to be used with all the basic data types, then what happens. If you provide parameterization type in Java, you can automate this work (there are many other benefits).

转载请注明原文地址:https://www.9cbs.com/read-4703.html

New Post(0)