CSC/ECE 517 Fall 2012/ch1 1w21 wi: Difference between revisions
mNo edit summary |
|||
(26 intermediate revisions by 2 users not shown) | |||
Line 183: | Line 183: | ||
Advantages that use of primitive data type offers are as follows: | Advantages that use of primitive data type offers are as follows: | ||
===Consistency=== | ===Consistency=== | ||
Many ideas of | Many ideas of Object Oriented Language originate in C and in C++, objects and primitive type also co-exist. When object-oriented constructs were grafted onto the C language, C’s data types remained as is. The motivation behind this is that Bjarne Stroustrup, C++’s designer, wanted to be as consistent as possible with the base C language, hoping to capitalize on programmers’ pre-C++ knowledge. | ||
<ref>http://www.research.ibm.com/people/a/alpert/ptch/ptch.html Primitive Types Considered Harmful</ref> | <ref>http://www.research.ibm.com/people/a/alpert/ptch/ptch.html Primitive Types Considered Harmful</ref> | ||
Line 212: | Line 212: | ||
Example: | Example: | ||
public void test1(){ | |||
int a=1,b=1; | |||
long startTime1 = System.currentTimeMillis(); | |||
for(int i=0;i<N;i++){ | |||
a=a+b; | |||
} | |||
long endTime1 = System.currentTimeMillis(); | |||
System.out.println(endTime1-startTime1); | |||
} | |||
public void test2(){ | |||
Integer c=new Integer(1); | |||
Integer d=new Integer(1); | |||
long startTime2 = System.currentTimeMillis(); | |||
for(int i=0;i<N;i++){ | |||
c=c+d; | |||
} | |||
long endTime2 = System.currentTimeMillis(); | |||
System.out.println(endTime2-startTime2) | |||
} | |||
Average execution time for test1 is: 1 mm | Average execution time for test1 is: 1 mm | ||
Line 245: | Line 243: | ||
Composite data type will cost more memory space than primitive data type in general. The reason is not only for the overheads generated by constructors, destructors etc, but also the feature of data alignment. <ref>http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding Data structure alignment</ref> | Composite data type will cost more memory space than primitive data type in general. The reason is not only for the overheads generated by constructors, destructors etc, but also the feature of data alignment. <ref>http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding Data structure alignment</ref> | ||
Example of data | Example of data alignment(C++) | ||
struct compose | |||
{ | |||
char name; | |||
int a; | |||
int b; | |||
} | |||
We all know the size of char is 1 byte, the size of int is 4 bytes. But the size of the struct compose as shown above is 12 bytes but not 9 bytes. | We all know the size of char is 1 byte, the size of int is 4 bytes. But the size of the struct compose as shown above is 12 bytes but not 9 bytes. | ||
Line 260: | Line 258: | ||
===Cannot be extended=== | ===Cannot be extended=== | ||
We can use class or array to | We can use a class or array to model the intricate data or functional structure. With the feature of class, programmers can create subtypes, modify their operations and even redefine them. In addition with the feature of class, programmers can make their variables unchangeable to any code outside the class, or make the implementation hidden to other code with call the methods. That means with composite data types, it will be more convenient for the programmers to handle sophisticated data and also make the code easier to maintain. | ||
In the following example, we create a Person type in RUBY. We defined a “talk” method to make a Person can talk about himself. We also hope to only make the variable “motherland” changeable by the code outside this class. | |||
Example: (in RUBY) | |||
class Person | |||
def initialize( name, age=18 ) | |||
@name = name | |||
@age = age | |||
@motherland = "US" | |||
end | |||
def talk | |||
puts "my name is "+@name+", age is "+@age.to_s | |||
if @motherland == "US" | |||
puts "I\'m American." | |||
else | |||
puts "I\'m International." | |||
end | |||
end | |||
attr_writer :motherland | |||
end | |||
Later when we need to create a “Student” type, we can just inherent the “Person” type and make some adjustment based on our needs. Here we redefine the “talk” method to make a student talk in a different way. | Later when we need to create a “Student” type, we can just inherent the “Person” type and make some adjustment based on our needs. Here we redefine the “talk” method to make a student talk in a different way. | ||
class Student < Person | |||
def talk | |||
puts "I am a student. my name is "+@name+", age is "+@age.to_s | |||
end | |||
end | |||
=== | ===Inability on handling large scale data=== | ||
Though it means more processing time, it is necessary for programmers to create composite type to handle the data whose length is too large or changing all the time. If we have a set or integers to maintain in memory, but the number of integers will change every | Though it means more processing time, it is necessary for programmers to create composite type to handle the data whose length is too large or changing all the time. If we have a set or integers to maintain in memory, but the number of integers will change every second, it will be impossible if we only use primitive types to handle. But we can make use of the “Arraylist” type in Java or other composite types. With the “Arraylist”, the programmers do not need to pay attention to the storage in the memory or the space it takes. The compiler of Java will reallocate more space automatically for the Arraylist if more elements are putted. <ref>http://docs.oracle.com/javase/1.5.0/docs/api/java/util/ArrayList.html Inability on handling large scale data</ref> | ||
Example of the insert and sorting methods of the Arraylist type in Java: | |||
ArrayListaList=newArrayList(); | |||
aList.Add("a"); | |||
aList.Add("b"); | |||
aList.Add("c"); | |||
aList.Add("d"); | |||
aList.Add("e"); | |||
aList.Sort(); | |||
===Limitation=== | ===Limitation on size=== | ||
For the primitive data types in most programming languages, there are some limits of the | For the primitive data types in most programming languages, there are some limits of the size. For example we can put an integer between -(2^16) and (2^16)-1 into an “int” in Java. That will be enough for some most of the cases but definitely not all of them. For example, the primitive types are not enough for a scientific calculation if we need an accuracy on 10^-500. And a more extreme case is to calculate the last 100 numbers for the PI has a 100 million length. | ||
For these situations, the programmers usually need to create a composite data type by themselves. They can even redefine the operators (“+”, “-”) to make the super-long float also can make addition, subtraction with ordinary operators instead of method calling. For more details to create a super long float please see reference. | For these situations, the programmers usually need to create a composite data type by themselves. They can even redefine the operators (“+”, “-”) to make the super-long float also can make addition, subtraction with ordinary operators instead of method calling. For more details to create a super long float please see reference.<ref>http://en.verysource.com/code/1285797_1/tlargefloat.h.html Limitation on size</ref> | ||
=== | ===Not supporting utility methods=== | ||
Most of the object oriented languages have certain utility methods associated with non primitive data types that ease their use as compared to primitive data types that do not have such methods.<ref>http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Integer.html Not supporting utility methods</ref>. | |||
An especially useful example is the method of parsing integers in a specified radix in Java (Integer.parseInt). | |||
parseInt("1100110", 2); //returns 102 | |||
parseInt("-0", 10); //returns 0 | |||
parseInt("-FF", 16); //returns -255 | |||
parseInt("99", 8); //throws a NumberFormatException | |||
From the above examples, it is obvious that “Integer” type not just support ordinary methods an integer should have (such as Integer.equals), but also some more utility methods which are helpful for the programmers. | From the above examples, it is obvious that “Integer” type not just support ordinary methods an integer should have (such as Integer.equals), but also some more utility methods which are helpful for the programmers. | ||
In addition, for some data structure which only take object but not primitive type (such as Vector in Java), it is necessary to box a primitive data type to a corresponding object. For example if a programmer needs to make use of a vector to store some integer, it is necessary to box the | In addition, for some data structure which only take object but not primitive type (such as Vector in Java), it is necessary to box a primitive data type to a corresponding object. For example if a programmer needs to make use of a vector to store some integer, it is necessary to box the ints and floats first: | ||
Vector v=new Vector(); | |||
int k=121; | |||
v.addElemt(new Integer(k)); | |||
===Functionality=== | ===Functionality=== | ||
Discussion of advantages and disadvantages should concerned the functionality you want to implement. For example if you want to operate a serials of int number. The performance of array may be better than int. <ref>http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH05/CH05-2.html | Discussion of advantages and disadvantages should concerned the functionality you want to implement. For example if you want to operate a serials of int number. The performance of array may be better than int. <ref>http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH05/CH05-2.html Composite datatypes</ref> | ||
[[File:1w1.gif]] | [[File:1w1.gif]] | ||
Line 339: | Line 338: | ||
To access an element of an array, you need a function that converts an array index into the address of the indexed element. For a single dimension array, this function is very simple. It is | To access an element of an array, you need a function that converts an array index into the address of the indexed element. For a single dimension array, this function is very simple. It is | ||
Element_Address = Base_Address + ((Index - Initial_Index) * Element_Size) | Element_Address = Base_Address + ((Index - Initial_Index) * Element_Size) | ||
However, If you use five primitive data type to implement the same function. It may cost more time for the cpu to search the address of each data. | However, If you use five primitive data type to implement the same function. It may cost more time for the cpu to search the address of each data. | ||
== Conclusion == | == Conclusion == | ||
In a nutshell, composite data type is more likely to stand out the features of object-oriented language and be widely used. However,primitive data type would not disappear in a foreseeable future because of its advantage in performance and use. It bridges the gap between novice programmers and experienced programmers by making powerful features basic to the language and makes coding simpler. Dealing with primitive objects is easier at times. | |||
== References == | == References == | ||
<references/> | <references/> |
Latest revision as of 03:29, 11 September 2012
Primitive objects in object-oriented languages
Introduction
In any programming language, the data type refers to the class of data which contains specific type or range of values. Data types are used along with variables used in the program. The data type tells us what kind of values the variable can store, what is the range of the values and how much space the values take in memory etc.
The primitive data types refer to the built-in data types which are supported by the programming language. Often 'data types' and 'primitive data types' are used interchangeably. But not all data types are primitive. Programming languages have some non-primitive data types or derived data types which are provided by the language by making use of its primitive data types.
The common built-in data types or primitive data types are integers, floating point numbers, characters, strings and boolean.
- Integers - Integers represent the whole numbers which can be positive or negative or zero, e.g. 9999, 0, -25, etc.
- Floating point numbers - Floating point numbers represent the numbers which are fractions or contain floating-decimal points, e.g. -3.002, 2.5, 22.0, etc.
- Characters - Characters represent any single letter, number, space, punctuation mark, or symbol that can be typed on a computer, e.g. 'a', '9', ' ', '!' , '\n', etc.
- Strings - Strings represent the sequences of characters or simply any text, e.g. "Hello!", "9 am to 6 pm", etc.
- Booleans - Booleans represent the true or false values. Sometimes, instead of true and false, 1 and 0 are used to represent the boolean values.
Many object-oriented programming languages provide support for primitive data types while some object-oriented programming languages provide support for primitive objects along with primitive types.
Definition
Primitive objects refer to the objects of built-in classes which provide more functionality than the primitive types. Some object-oriented programming languages provide support for only primitive objects (i.e., in such languages all primitive types are objects).
Different object-oriented programming languages implement these primitive data types and primitive objects in a different manner.
Primitive objects in different OO languages
Java
In Java, there are 8 primitive types: boolean, char, byte, short, int, long, float, and double. <ref>http://www.cafeaulait.org/course/week2/02.html Java's Primitive Data Types</ref>
For each of these primitive types, Java provides wrapper classes to create primitive objects which wrap the primitive data values. A wrapper not only contains the primitive data value, but it also defines properties and methods that can be used to manipulate that data. In Java, the primitive values are not implicitly converted to primitive objects. Instead, methods are provided for doing explicit conversion. The primitive objects are stored on heap in memory while the variables containing primitive values are stored on stack.<ref>http://www.informit.com/articles/article.aspx?p=31755&seqNum=8 Stack and Heap memory</ref>
Primitive Type | Wrapper Class | Size |
---|---|---|
boolean | Boolean | 1-bit |
char | Character | 16-bit |
byte | Byte | 8-bit |
short | Short | 16-bit |
int | Integer | 32-bit |
long | Long | 64-bit |
float | Float | 32-bit |
double | Double | 64-bit |
Example:
int i = 10; int ii = 20; Integer I = new Integer(i); Integer II = new Integer(ii); System.out.println(I+II); System.out.println(I.equals(II));
Output:
30 false
To find out if these wrapper classes are primitive or not, we can use the isPrimitive() method.
System.out.println(INTEGER.TYPE.isPrimitive()); System.out.println(BOOLEAN.TYPE.isPrimitive());
Output:
true true
In Java, the comparison operations work in the same way on the primitive objects as well as any other class objects but different on the primitive types. The == operator when used on objects checks whether they refer to the same object but when used on variables of primitive types checks whether they contain the same value.<ref>http://leepoint.net/notes-java/data/expressions/22compareobjects.html Comparisons in Java</ref>
Example:
int i = 10; int ii = 10; Integer I = new Integer(i); Integer II = new Integer(ii); System.out.println(I==II); System.out.println(i==ii);
Output:
false true
C#
C# is a strongly typed language, where it is necessary to declare the data type of a variable and also be aware of the data type conversion. C# provides a significant number of primitive data types.<ref>http://condor.depaul.edu/sjost/ndp/notes/cs1/CSDatatypes.htm C# Primitive Datatypes</ref><ref>http://www.java2s.com/Tutorial/CSharp/0040__Data-Type/PrimitivesinC.htm Primitives in C#</ref>
Because C# represents all primitive data types as objects, it is possible to call an object method on a primitive data type. For example:
static void Main() { int x = 7; object o = x; System.Console.WriteLine(o.ToString()); }
Some data types (e.g. Decimal and String) can appear like primitives at first sight, but are actually not. So it is important to exercise caution before making such assumptions. To test whether a particular type is a primitive or not you can use the property Type.IsPrimitive.
Consider the following example:
if (t.IsPrimitive) // where t is the type { // Is Primitive } else if (t == typeof(Decimal)) { // Is Decimal } else if (t == typeof(String)) { // Is String } else { // Other type }
JavaScript
There are 5 primitive data types in JavaScript: string, number, boolean, null and undefined. <ref>http://oreilly.com/javascript/excerpts/learning-javascript/javascript-datatypes-variables.html JavaScript Data Types and Variables</ref>
For string, number and boolean values, there are corresponding classes just like in Java to create primitive objects which wrap the primitive values.
Primitive Type | Wrapper Class |
---|---|
string | String |
number | Number |
boolean | Boolean |
In JavaScript, the primitive value is implicitly converted to a primitive object whenever someone tries to access a property or invoke a method on the primitive value and the primitive object is used in place of the primitive value. Since the object contains properties and methods, the use of primitive value as an object succeeds. After the property is accessed or the method is processed, the primitive object is no longer needed and hence discarded. The same is true for the other primitive types and their corresponding primitive objects.
Example:
var upperCaseString = "APPLE"; var lowerCaseString = upperCaseString.toLowerCase(); // assigns string "apple" to lowerCaseString var s = "Hello" var len = s.length; // assigns value 5 to len
Ruby
Since Ruby is a pure object oriented language, everything in Ruby is an object. Hence, all primitive types such as integers, floating point numbers, strings, are objects of a built-in class.<ref>http://ruby-doc.org/docs/ProgrammingRuby/html/builtins.html Classes in Ruby</ref> All integers are primitive objects of either class Fixnum or Bignum. A numeric literal with a decimal point and/or an exponent is a primitive object of Float. Single quoted literals and double quoted literals are primitive objects of String.
Example:
puts 10.class puts 7.45.class puts 'hi'.class puts "hello".class
Output:
Fixnum Float String String
This indicates that 10 is converted into an object of type Fixnum, 7.45 is converted into an object of type Float, 'hi' and "hello" are both converted into an object of type String.
Since all primitive types in Ruby are objects, we should be able to call methods of the Object class on them. Let us demonstrate the same for integer and float using the following example:
a=10 puts a.to_f b=20.5 puts b.to_i
Output:
10.0 20
Advantages of primitives
Advantages that use of primitive data type offers are as follows:
Consistency
Many ideas of Object Oriented Language originate in C and in C++, objects and primitive type also co-exist. When object-oriented constructs were grafted onto the C language, C’s data types remained as is. The motivation behind this is that Bjarne Stroustrup, C++’s designer, wanted to be as consistent as possible with the base C language, hoping to capitalize on programmers’ pre-C++ knowledge. <ref>http://www.research.ibm.com/people/a/alpert/ptch/ptch.html Primitive Types Considered Harmful</ref>
Simplicity
It is simple and intuitive to use primitive data type in basic utility.
Example:
1. Recurring number in a loop.(C++)
for(int i=0;i<10;i++)
2. Temp variables that record only one or two data.
Time performance
Processing primitive data type operation would be faster than processing composite data type in most cases. There are several reasons result in this differences.
- Hardware processing discrepancy
Many primitive data operations (like integer addition) can be performed as a single machine instruction while processing of composite data type would be transformed to a combination of primitive data types. eg. Some processors offer specific instructions to process sequences of characters with a single instruction.
Example:
Difference in data fetching between primitive data and composite data in Java
- Allocating and Garbage collection overhead for composite data type<ref>http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)#Performance_implications Garbage collection</ref><ref>http://www.ibm.com/developerworks/java/library/j-jtp01274/index.html Java theory and practice: Garbage collection and performance</ref>
There are many initialization operations for composite data type that will generate high overheads for example boxing and unboxing, constructors and destructors.Composite data type like class will also have garbage collection overheads that may be beyond the control of the programmer and can sometimes lead to performance problems. For example, commonly used stop-the-world garbage collectors, which pause program execution at arbitrary times, may make garbage collection inappropriate for some embedded systems, high-performance server software, and applications with real-time needs.In the early days of Java technology, allocating objects got a high overheads. There were lots of articles advising developers to avoid creating temporary objects unnecessarily because allocation (and the corresponding garbage-collection overhead) was expensive. Although the costs have been greatly reduced nowadays. It still much slower than primitive data type which don’t have such troubles.
Example:
public void test1(){ int a=1,b=1; long startTime1 = System.currentTimeMillis(); for(int i=0;i<N;i++){ a=a+b; } long endTime1 = System.currentTimeMillis(); System.out.println(endTime1-startTime1); }
public void test2(){ Integer c=new Integer(1); Integer d=new Integer(1); long startTime2 = System.currentTimeMillis(); for(int i=0;i<N;i++){ c=c+d; } long endTime2 = System.currentTimeMillis(); System.out.println(endTime2-startTime2) }
Average execution time for test1 is: 1 mm
Average execution time for test2 is: 702mm
(test case number: 1000 times )
Space performance
Composite data type will cost more memory space than primitive data type in general. The reason is not only for the overheads generated by constructors, destructors etc, but also the feature of data alignment. <ref>http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding Data structure alignment</ref>
Example of data alignment(C++)
struct compose { char name; int a; int b; }
We all know the size of char is 1 byte, the size of int is 4 bytes. But the size of the struct compose as shown above is 12 bytes but not 9 bytes.
Disdvantages of primitives
The various disadvantages of using the primitive data types are as follows:
Cannot be extended
We can use a class or array to model the intricate data or functional structure. With the feature of class, programmers can create subtypes, modify their operations and even redefine them. In addition with the feature of class, programmers can make their variables unchangeable to any code outside the class, or make the implementation hidden to other code with call the methods. That means with composite data types, it will be more convenient for the programmers to handle sophisticated data and also make the code easier to maintain.
In the following example, we create a Person type in RUBY. We defined a “talk” method to make a Person can talk about himself. We also hope to only make the variable “motherland” changeable by the code outside this class.
Example: (in RUBY)
class Person def initialize( name, age=18 ) @name = name @age = age @motherland = "US" end def talk puts "my name is "+@name+", age is "+@age.to_s if @motherland == "US" puts "I\'m American." else puts "I\'m International." end end attr_writer :motherland end
Later when we need to create a “Student” type, we can just inherent the “Person” type and make some adjustment based on our needs. Here we redefine the “talk” method to make a student talk in a different way.
class Student < Person def talk puts "I am a student. my name is "+@name+", age is "+@age.to_s end end
Inability on handling large scale data
Though it means more processing time, it is necessary for programmers to create composite type to handle the data whose length is too large or changing all the time. If we have a set or integers to maintain in memory, but the number of integers will change every second, it will be impossible if we only use primitive types to handle. But we can make use of the “Arraylist” type in Java or other composite types. With the “Arraylist”, the programmers do not need to pay attention to the storage in the memory or the space it takes. The compiler of Java will reallocate more space automatically for the Arraylist if more elements are putted. <ref>http://docs.oracle.com/javase/1.5.0/docs/api/java/util/ArrayList.html Inability on handling large scale data</ref>
Example of the insert and sorting methods of the Arraylist type in Java:
ArrayListaList=newArrayList(); aList.Add("a"); aList.Add("b"); aList.Add("c"); aList.Add("d"); aList.Add("e"); aList.Sort();
Limitation on size
For the primitive data types in most programming languages, there are some limits of the size. For example we can put an integer between -(2^16) and (2^16)-1 into an “int” in Java. That will be enough for some most of the cases but definitely not all of them. For example, the primitive types are not enough for a scientific calculation if we need an accuracy on 10^-500. And a more extreme case is to calculate the last 100 numbers for the PI has a 100 million length.
For these situations, the programmers usually need to create a composite data type by themselves. They can even redefine the operators (“+”, “-”) to make the super-long float also can make addition, subtraction with ordinary operators instead of method calling. For more details to create a super long float please see reference.<ref>http://en.verysource.com/code/1285797_1/tlargefloat.h.html Limitation on size</ref>
Not supporting utility methods
Most of the object oriented languages have certain utility methods associated with non primitive data types that ease their use as compared to primitive data types that do not have such methods.<ref>http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Integer.html Not supporting utility methods</ref>.
An especially useful example is the method of parsing integers in a specified radix in Java (Integer.parseInt).
parseInt("1100110", 2); //returns 102 parseInt("-0", 10); //returns 0 parseInt("-FF", 16); //returns -255 parseInt("99", 8); //throws a NumberFormatException
From the above examples, it is obvious that “Integer” type not just support ordinary methods an integer should have (such as Integer.equals), but also some more utility methods which are helpful for the programmers.
In addition, for some data structure which only take object but not primitive type (such as Vector in Java), it is necessary to box a primitive data type to a corresponding object. For example if a programmer needs to make use of a vector to store some integer, it is necessary to box the ints and floats first:
Vector v=new Vector(); int k=121; v.addElemt(new Integer(k));
Functionality
Discussion of advantages and disadvantages should concerned the functionality you want to implement. For example if you want to operate a serials of int number. The performance of array may be better than int. <ref>http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH05/CH05-2.html Composite datatypes</ref>
To access an element of an array, you need a function that converts an array index into the address of the indexed element. For a single dimension array, this function is very simple. It is Element_Address = Base_Address + ((Index - Initial_Index) * Element_Size)
However, If you use five primitive data type to implement the same function. It may cost more time for the cpu to search the address of each data.
Conclusion
In a nutshell, composite data type is more likely to stand out the features of object-oriented language and be widely used. However,primitive data type would not disappear in a foreseeable future because of its advantage in performance and use. It bridges the gap between novice programmers and experienced programmers by making powerful features basic to the language and makes coding simpler. Dealing with primitive objects is easier at times.
References
<references/>