ARRAYLIST VS LINKEDLIST

Core Dumped
16 Mar 202421:20

Summary

TLDRThis video script explores the intricacies of arrays in programming, contrasting their behavior across different languages. It explains that systems programming languages require explicit type and size specification for arrays, whereas scripting languages like JavaScript and Python offer more flexibility, with JavaScript arrays being essentially hashmaps. The script delves into memory allocation, indexing, and the trade-offs between dynamic sizing and cache efficiency, highlighting data structures like linked lists and array lists to overcome array limitations.

Takeaways

  • πŸ“š Arrays are fundamental in programming, but their functionality varies by language, with systems programming languages requiring explicit type and size specification.
  • πŸ” Scripting languages offer more flexibility with arrays, allowing dynamic resizing and the inclusion of diverse data types.
  • πŸ› οΈ In systems programming, the inability to expand arrays and store different data types is due to the need for contiguous memory allocation and the risk of overwriting other values.
  • πŸ“ Arrays use indexing, starting with zero, to access elements, which is a concept inherited from C and widely used due to its pointer arithmetic simplicity.
  • 🧠 The base address of an array is crucial for indexing, as it helps calculate the memory address of any element by adding the product of the element's size and its index to this base address.
  • 🚫 Accessing elements outside the array's bounds can lead to segmentation faults or undefined behavior if the accessed memory is within the process's boundaries.
  • πŸ”„ To overcome array limitations, data structures like linked lists and array lists are used, providing dynamic sizing but with trade-offs in caching efficiency and performance.
  • πŸ”— Linked lists allow dynamic sizing but suffer from poor cache performance due to scattered elements in memory.
  • πŸ“ˆ Array lists combine the benefits of arrays with dynamic sizing, using a capacity that can grow as needed, though this can incur performance costs during resizing.
  • πŸ”‘ JavaScript arrays are implemented as hashmaps rather than traditional arrays, allowing for flexible indexing and efficient memory use.
  • 🌐 The choice of data structure depends on the specific requirements of the application, including performance, memory usage, and the need for dynamic resizing.

Q & A

  • What is the primary difference between arrays in systems programming languages and scripting languages?

    -In systems programming languages, arrays require the specification of both the type and size of elements they will store, while scripting languages offer more flexibility, allowing for the addition of an unlimited number of elements and the inclusion of diverse data types within the same array.

  • Why can't arrays in systems programming languages inherently expand to accommodate more elements?

    -Arrays in systems programming languages can't inherently expand because adding elements without considering the memory could potentially overwrite other necessary values. This is why such languages often lack methods like append, insert, or push, which are common in scripting languages.

  • How does the memory address play a role in accessing elements in an array?

    -Memory addresses are crucial for accessing array elements because the computer accesses values in terms of memory addresses rather than variables. The base address of the array is used to calculate the memory address of a specific element by adding the product of the element's size and its index to the base address.

  • What is the main advantage of using linked lists over arrays in terms of size flexibility?

    -Linked lists provide a dynamically sized list of elements by storing them wherever there is available memory space. They use nodes that contain an item and a pointer to the next node, which allows for the addition of new elements without the need to know the total number of elements in advance.

  • Why are arrays preferred over linked lists in terms of cache performance?

    -Arrays are preferred for cache performance because they keep elements compacted in memory, which increases the chances of cache hits. Linked lists, with their elements scattered throughout memory, are less likely to be cached efficiently due to their non-contiguous nature.

  • What is the main idea behind an ArrayList in contrast to a simple array?

    -An ArrayList is a data type that includes a reference to an array along with additional information about the array, such as capacity and length. It handles dynamic resizing, allowing the array to grow as elements are added, while also providing constant time access to elements by index.

  • How does an ArrayList manage to grow its size dynamically while maintaining performance?

    -When an ArrayList's array is full, it allocates a new array with a larger capacity, typically twice the size of the full array. It then copies all the values from the old array to the new one, updates the reference and capacity, and finally adds the new element, ensuring dynamic growth without significant performance degradation.

  • What is the trade-off when considering the use of linked lists for removing elements?

    -While removing an element from a linked list is faster because it only requires updating pointers and does not involve shifting subsequent elements, finding the element to remove in the first place can be slow due to the need to traverse the list from the head to the desired position.

  • How do scripting languages like Python handle arrays differently from systems programming languages?

    -Scripting languages like Python use a specialized data structure, such as a PyListObject, which is an array of object pointers. This allows for the addition of elements of different types and simulates the behavior of an array without the strict type and size limitations of systems programming languages.

  • What is the underlying structure of a JavaScript array, and how does it differ from traditional arrays?

    -JavaScript arrays are actually implemented as hashmaps, where keys correspond to indexes. This allows for the addition of elements at any index without the need to allocate memory for all the positions in between, providing a flexible and efficient way to handle arrays.

Outlines

00:00

πŸ“š Fundamentals of Arrays and Their Variability Across Languages

This paragraph introduces the concept of arrays as a basic tool for developers, emphasizing how their behavior can differ significantly across programming languages. It explains the necessity in systems programming languages to define both the type and size of array elements, contrasting this with the flexibility offered by scripting languages, which allow dynamic resizing and the mixing of data types within a single array. The paragraph sets the stage for a discussion on why scripting languages can handle arrays differently and hints at the topic of 'real arrays' in JavaScript, which will be debunked later in the video.

05:01

πŸ” Deep Dive into Array Indexing and Memory Management

The second paragraph delves into the technical aspects of array indexing and memory management. It describes how arrays are stored contiguously in memory and the potential issues that can arise from dynamic resizing, such as overwriting other memory values. The explanation covers the low-level operations of how CPUs access memory addresses and the implications of incorrect memory access due to array resizing. It also touches on the lack of inherent size information in arrays at a low level of abstraction, leading to potential out-of-bounds access errors, and contrasts this with the higher-level abstractions provided by scripting languages.

10:03

πŸ› οΈ Addressing Array Limitations with Data Structures

This paragraph discusses the limitations of arrays and how they are addressed through the use of data structures. It introduces linked lists as a simple method for creating a dynamically sized list, explaining the concept of nodes and how they are used to store elements in available memory spaces. The paragraph also covers the drawbacks of linked lists, particularly their inefficiency for caching due to scattered memory allocation. It then introduces array lists (or dynamic arrays) as a compromise between the flexibility of linked lists and the compact memory usage of arrays, explaining how they manage memory and grow in size to accommodate more elements.

15:03

πŸ”„ Performance Implications of Array and List Operations

The fourth paragraph examines the performance implications of operations on arrays and lists. It discusses the time complexity of adding and removing elements from both data structures, highlighting the constant time complexity for accessing elements in an array list due to its internal array storage. The paragraph also addresses the inefficiency of accessing elements in a linked list due to its linear time complexity and the caching issues associated with non-contiguous memory allocation. It further explores the impact of shifting elements in an array when removing them and the advantages of knowing the approximate number of elements in advance to optimize performance.

20:05

🌐 Generics and Scripting Languages' Unique Approach to Arrays

The final paragraph explores how scripting languages, such as Python and JavaScript, handle arrays differently from systems programming languages. It explains that scripting languages use interpreters or virtual machines to emulate the behavior of arrays, which are implemented as specialized data structures like Python's 'py list object' or JavaScript's hashmap approach. The paragraph highlights the flexibility of these implementations, which allow for arrays to hold elements of different types and to dynamically resize without the performance penalties associated with systems programming languages. It also touches on the performance considerations and the clever solutions employed by these languages to overcome the limitations of traditional arrays.

Mindmap

Keywords

πŸ’‘Arrays

Arrays are a fundamental data structure in programming, consisting of a collection of elements, each identified by an index. In the context of the video, arrays are discussed in terms of their behavior across different programming languages. Systems programming languages require the specification of element type and size, while scripting languages offer more flexibility. The video explains how arrays are managed in memory and the implications of adding elements to them, which is crucial for understanding the limitations and capabilities of arrays in various programming scenarios.

πŸ’‘Indexing

Indexing refers to the method of accessing elements in an array using their position or index. The video explains that arrays in programming start with the first position usually at index zero, and each subsequent position is identified by incrementing the index. Indexing is essential for retrieving and modifying elements within an array. The video uses indexing to illustrate how elements are accessed in memory and the potential issues that can arise from accessing elements outside the array's bounds.

πŸ’‘Memory Address

A memory address is a reference to a specific location in a computer's memory where data is stored. The video discusses how computers access values in terms of memory addresses rather than variables. When an array is used, the initial memory addresses of the elements are overwritten, which can lead to errors if subsequent operations try to access these values. Understanding memory addresses is key to comprehending how arrays function at a low level and the potential pitfalls of dynamic memory allocation.

πŸ’‘Segmentation Fault

A segmentation fault is an error that occurs when a program attempts to access memory that it's not allowed to. In the video, this concept is used to illustrate what happens when an array is accessed beyond its allocated memory space. The operating system detects such attempts and terminates the process to prevent corruption of memory, which is a critical concept in understanding system stability and array management.

πŸ’‘Data Structures

Data structures are specialized formats for organizing, processing, and storing data. The video discusses how data structures like linked lists and array lists (dynamic arrays) overcome the limitations of traditional arrays, such as fixed size and type restrictions. These data structures are designed to handle dynamic resizing and allow for more flexibility in storing different data types, which is essential for efficient data management in programming.

πŸ’‘Caching

Caching is the process of storing copies of frequently accessed data in a small, fast memory area called cache, which is embedded directly in the CPU. The video explains how caching affects the performance of data structures like arrays and linked lists. Since arrays store elements contiguously in memory, they are more likely to be cached, leading to faster data retrieval. This concept is crucial for understanding the performance implications of different data storage methods.

πŸ’‘Generics

Generics are a feature in some programming languages that allow data structures to be defined for use with any data type. The video touches on how generics enable the creation of data structures that can handle different types of elements without needing to create separate versions for each type. This is particularly relevant in the discussion of how languages like Rust and Java handle the creation of arrays and other data structures that can store various types of elements.

πŸ’‘Interpreters

Interpreters are programs that directly execute instructions written in a programming or scripting language without previously compiling them into machine code. The video discusses how scripting languages like Python and JavaScript use interpreters or virtual machines to handle arrays. These interpreters create specialized data structures to simulate the behavior of arrays, which is a key concept in understanding how dynamic languages manage data differently from compiled languages.

πŸ’‘HashMaps

A hashmap is a data structure that associates keys with values, allowing for efficient retrieval of values based on their keys. In the context of the video, it is mentioned that JavaScript arrays are actually implemented as hashmaps. This means that when elements are added to a JavaScript array, the engine creates a hashmap where the keys are the indexes. This implementation allows for dynamic resizing and the ability to have non-sequential indexes, which is a significant departure from traditional array behavior.

πŸ’‘Dynamic Resizing

Dynamic resizing is the ability of a data structure to automatically adjust its capacity to accommodate more elements as needed. The video explains how array lists handle dynamic resizing by allocating a new, larger array when the current one is full and copying the elements to the new array. This process is crucial for understanding how some data structures can grow in size while maintaining efficient memory usage and performance.

Highlights

Arrays in different programming languages exhibit varying behaviors, with systems programming languages requiring specification of element type and size, while scripting languages offer flexibility in element addition and data type diversity within the same array.

In systems programming, arrays are allocated with fixed size and type to prevent overwriting of adjacent memory values, explaining the lack of dynamic methods like append or push.

Scripting languages handle arrays differently, allowing dynamic resizing and mixed data types, which is contrasted with systems programming languages.

The video explores why JavaScript arrays are not 'real' arrays, but rather hashmaps, a unique approach to array handling.

Arrays are simple data structures with elements indexed from zero, allowing for retrieval and modification based on position.

Memory management is crucial as adding elements to an array can overwrite other values if not carefully managed, a common issue in low-level programming.

The distinction between array elements in memory and their representation in hardware is highlighted, emphasizing the importance of base address for indexing.

C programming language introduced syntax to simplify pointer arithmetic, influencing most major programming languages to adopt zero-based indexing.

Arrays in low-level languages do not contain inherent size information, leading to potential risks of accessing out-of-bound elements.

Linked lists offer a dynamically sized alternative to arrays, using nodes that contain elements and pointers to the next node.

Linked lists can suffer from caching inefficiencies due to scattered elements in memory, contrasting with the compact nature of arrays.

ArrayLists combine the benefits of arrays with dynamic resizing, using additional information about capacity and length to manage elements.

The process of resizing an ArrayList involves heap allocation and element copying, which can have performance implications.

ArrayLists provide constant time access for elements, unlike linked lists, which require linear time complexity for indexing.

Removal operations in arrays require shifting elements, which can be time-consuming, while linked lists can remove elements with constant time complexity by adjusting pointers.

Generic types in programming languages like Rust are handled differently, with the compiler generating specific code for each instance of a generic type at compile time.

Java handles custom types differently from primitive types in arrays, using an array of pointers to manage non-primitive elements.

Python's implementation uses a py list object, an array of object pointers, allowing for diverse data types within the same list.

The video concludes by revealing that JavaScript arrays are implemented as hashmaps rather than traditional arrays, a clever solution with unique memory management.

Transcripts

play00:00

arrays are a fundamental tool for

play00:01

developers but their behavior can vary

play00:03

significantly depending on the

play00:04

programming language being used in

play00:07

systems programming languages it's

play00:08

necessary to specify both the type and

play00:10

size of elements the arrays will store

play00:13

scripting languages offer more

play00:14

flexibility allowing for the addition of

play00:16

an unlimited number of elements while

play00:18

permitting the inclusion of diverse data

play00:20

types within the same array so our

play00:23

scripting languag is just better in

play00:25

today's video we'll look at how

play00:26

scripting languages handle this issue

play00:28

why it exists and how lower level

play00:30

programming languages overcome it we'll

play00:32

also discuss why JavaScript arrays

play00:34

aren't even real

play00:35

arrays hi friends my name is George and

play00:38

this is core

play00:40

dumped before starting I want to send

play00:42

special thanks to the first members of

play00:44

this Channel and to today's sponsor

play00:46

codec Crafters more about them later in

play00:48

the

play00:49

video arrays are quite simple they

play00:52

contain elements each occupying a

play00:54

position in programming the first

play00:56

position is usually zero the second is

play00:59

one and so for these positions are

play01:01

called indexes which we use to retrieve

play01:04

and change the element at that position

play01:05

in the array at a low level the idea is

play01:09

to maintain each array element adjacent

play01:11

to one another in memory but there's a

play01:13

problem here since memory also holds

play01:16

other values arrays should not

play01:17

inherently expand disregarding this fact

play01:20

and adding elements could potentially

play01:22

overwrite other values that might be

play01:24

required later in this example

play01:26

immediately after adding elements to the

play01:28

array a numeric Rec addition operation

play01:31

follows the problem here is that

play01:33

computers don't access values in terms

play01:35

of variables but in terms of memory

play01:38

addresses when the operation is executed

play01:40

the machine code produced by the

play01:42

compiler instructs the CPU to add the

play01:44

numbers located in the specified memory

play01:46

addresses which is right because it

play01:48

refers to where both values were

play01:49

initially stored however since both

play01:52

values were overwritten by the array the

play01:54

CPU will execute the addition between

play01:56

incorrect

play01:58

values as you can see see this presents

play02:01

a significant issue one that is commonly

play02:03

addressed by simply not permitting

play02:05

programmers to add elements to arrays

play02:08

this explains why arrays and systems

play02:09

programming languages lack methods such

play02:11

as append insert or push as seen in

play02:15

Python JavaScript or other scripting

play02:17

languages apart from the fact that

play02:19

arrays are not objects at low level

play02:22

let's see how systems programming

play02:23

languages enforce this basic

play02:25

rule when the array is declared we

play02:28

specify both the type of elements it

play02:30

will contain and its length indicating

play02:32

how many of those specific elements the

play02:34

array will contain the reason behind the

play02:36

inability to have different types within

play02:38

the same array lies in the use of this

play02:40

information to determine the required

play02:42

memory space for the array the type

play02:45

specification informs the compiler about

play02:47

the number of bytes each element

play02:48

occupies in memory by multiplying this

play02:51

value by the array's length the precise

play02:53

amount of memory required to store the

play02:55

entire array can be

play02:56

determined whether the array is stored

play02:59

in the stack or the the Heap this

play03:00

information is essential to know how

play03:02

much to move the stack pointer and

play03:04

allocating a sufficiently large memory

play03:06

space respectively you can check out my

play03:08

two previous videos for more details

play03:10

about

play03:12

this okay here you see a clear

play03:14

distinction between elements but

play03:16

remember this is only an illustration in

play03:19

the bare Hardware things look more like

play03:21

this and the only information available

play03:24

to us is the memory address where the

play03:25

array begins referred to as the Base

play03:28

address

play03:30

this is how indexing Works since we know

play03:32

the number of bytes each element

play03:34

occupies within the array when we want

play03:36

to access a specific element we can

play03:38

calculate the memory address where that

play03:40

element starts to achieve this we add

play03:42

the product of the element's size and

play03:44

its position referred to as the index to

play03:47

the memory Base

play03:50

address this explains why we utilize

play03:53

zero instead of one for the first

play03:55

element when attempting to access the

play03:57

initial element moving the pointer is

play03:59

unnecessary

play04:00

as far as I know the C programming

play04:02

language introduced this sugar syntax to

play04:04

simplify a process known as pointer

play04:06

arithmetic and the reason why almost any

play04:08

major programming language out there

play04:10

uses indexing starting with zero is

play04:12

because their syntax is inspired by

play04:15

C pointer arithmetic is a concept very

play04:18

important in C I want to make a video

play04:20

about it in the future but for now

play04:23

here's a very good explanation about it

play04:24

from one of my favorite

play04:27

channels to conclude this section of the

play04:29

video it's important to note that the

play04:31

array itself does not contain

play04:33

information about its size at this level

play04:35

of abstraction an array is simply a

play04:37

block of memory lacking an inherent

play04:39

property such as length to verify the

play04:42

validity of an index being passed

play04:44

therefore without caution there's a high

play04:46

likelihood of attempting to access an

play04:48

element outside the actual bounds of the

play04:51

array when this happens there are two

play04:54

possible scenarios if the array is

play04:57

allocated at or near the end of the

play04:59

memory Gra granted to our process the

play05:01

operating system will detect attempts to

play05:03

access memory locations beyond our

play05:04

allotted

play05:05

space in such instances the operating

play05:08

system terminates the execution of our

play05:10

process resulting in a segmentation

play05:12

fault

play05:13

error if the array is allocated further

play05:16

away from the memory limit the operating

play05:18

system will not terminate the execution

play05:20

since we'll be accessing memory within

play05:22

our process's

play05:23

boundaries in this example even though

play05:26

we're accessing values out of the array

play05:28

it doesn't matter if that region is

play05:29

holding other value as everything is

play05:31

ones and zeros at low level the accessed

play05:34

region will be interpreted as if it were

play05:36

of the same type as the elements within

play05:37

the array allowing the program to

play05:39

continue

play05:41

execution you might think the second

play05:43

situation is the best case scenario

play05:45

because our program won't crash but stop

play05:48

the video for a second and think about

play05:49

it is it preferable for our program to

play05:52

crash or to continue execution under the

play05:54

assumption that the array actually had a

play05:56

Fifth Element I mean just imagine if

play05:59

this array represents purchases made by

play06:01

an

play06:02

individual okay now that we understand

play06:04

why arrays can't grow and why they are

play06:06

limited to storing only one kind of

play06:07

element at a time let's address the

play06:09

non-dynamic size Behavior by utilizing

play06:12

data structures but before that a quick

play06:14

message from today's sponsor if you

play06:16

follow this channel you're probably the

play06:18

kind of person who loves to understand

play06:20

how things work if you've ever wondered

play06:22

how your favorite developer tools like

play06:24

git redus or Docker work under the hood

play06:26

the best way to gain that knowledge is

play06:28

by recreating them yourself

play06:30

codec Crafters offers challenge-based

play06:32

courses that guide you through crafting

play06:33

these tools step by step if there is a

play06:36

concept that you don't know their

play06:38

platform will provide you with the

play06:39

necessary information to help you

play06:40

continue progressing the best part is

play06:43

you can start the courses by choosing a

play06:45

programming language of your choice so

play06:47

you're not only recreating these tools

play06:49

you're also improving your programming

play06:51

skills with the language you prefer if

play06:53

you're interested and want to support me

play06:55

you can sign up for code crafters and

play06:57

choose the plan that best suits you

play06:58

using the referral Link in the

play07:00

description below I've been thinking

play07:02

about solving these challenges on stream

play07:04

but I don't know if that's something you

play07:05

guys are interested in I'm looking

play07:07

forward to hearing your thoughts in the

play07:08

comments you can follow me on Twitter

play07:10

for updates on this matter and now back

play07:13

to the video data structures in case you

play07:17

haven't watched my previous video let me

play07:19

quickly explain linked lists to

play07:21

you the simplest method to obtain a

play07:24

dynamically sized list of elements is by

play07:26

storing them wherever there is available

play07:28

memory space

play07:30

to do this we create a custom data type

play07:32

called a node a node contains one item

play07:35

and one pointer the item represents the

play07:38

element of the list and the pointer

play07:39

directs to the address where the next

play07:41

node containing the next element resides

play07:43

in

play07:45

memory we only need to maintain a

play07:47

reference to the first node typically in

play07:49

the stack when we wish to add a new

play07:52

element to the list we search for an

play07:53

available space big enough for that node

play07:56

then we update the last node of the list

play07:58

to point to the new node which becomes

play08:00

the last one this approach eliminates

play08:03

the concern about the list of elements

play08:06

growing however linked lists suffer from

play08:09

a significant drawback they are not

play08:11

efficient for caching

play08:12

data to give you some context cache is a

play08:15

small extremely fast memory embedded

play08:18

directly in the CPU its sole purpose is

play08:21

to store a copy of a small region of

play08:22

memory when the CPU requires data

play08:25

fetching it from memory can take a

play08:27

considerable amount of time relative to

play08:29

the CPU speed of course but if the

play08:32

required value is cached the process of

play08:34

loading data into registers can be

play08:36

orders of magnitude faster when the data

play08:39

required for a CPU operation is found in

play08:41

Cache we refer to it as a cach hit

play08:45

conversely if the data is not in Cache

play08:47

we refer to it as a cach

play08:51

Miss due to its limited size the cache

play08:54

cannot store a copy of the entire memory

play08:56

for Optimal Performance our data should

play08:58

ideally be as compact as possible

play09:01

allowing it to fit into the cach and

play09:02

increasing the chances of cash

play09:05

hits this may sound like a trivial

play09:07

concept but when our programs are

play09:09

optimized to take advantage of caching

play09:11

their performance can increase

play09:15

tremendously furthermore in modern

play09:18

Computing cash isn't just a feature it's

play09:20

the norm therefore cash misses will be

play09:23

our primary concern for the remainder of

play09:25

this

play09:26

video the

play09:28

problem as linked lists elements are

play09:30

scattered throughout memory they are

play09:32

very unlikely to be

play09:34

cached here arrays are a clear winner as

play09:37

they keep elements compacted in

play09:40

memory but now we are back where we

play09:42

started arrays are too Limited in

play09:45

functionalities here's where array lists

play09:47

come into play as the name suggests we

play09:49

still use arrays but in a smarter way

play09:52

instead of just holding a reference to

play09:54

an array we create a data type that

play09:56

includes that reference plus additional

play09:58

information about the

play09:59

array when created this data type

play10:02

allocates memory for an empty

play10:05

array the capacity attribute indicates

play10:07

how many elements the array can hold

play10:09

which in this example is

play10:12

five the length attribute indicates how

play10:14

many elements the array contains at any

play10:16

given time initially since the array is

play10:19

allocated memory with no elements its

play10:21

length is

play10:23

zero the idea behind this data type is

play10:25

that it'll handle the array for us so it

play10:28

comes with some Associated

play10:31

methods as the name suggests the push

play10:34

method accepts a value and adds it to

play10:35

the array every time we add an element

play10:38

to the array the length attribute

play10:41

increases but note that eventually the

play10:43

array will be filled with

play10:45

elements when this happens if we try to

play10:48

add more elements we'll exceed the

play10:50

arrays bounds which is exactly what

play10:52

we're trying to prevent by using this

play10:53

data

play10:55

structure to prevent adding values

play10:57

beyond the array's capacity we need to

play10:59

check whether the array is full before

play11:01

adding a new

play11:02

element this is as simple as verifying

play11:05

if the length is equal to the

play11:08

capacity if the array is not full we can

play11:11

safely add the value of the new

play11:26

element however if the array is full

play11:30

we need to allocate a larger array to

play11:31

accommodate more elements typically we

play11:34

allocate a new array twice as long as

play11:36

the one that is

play11:37

full during this process we update both

play11:40

the capacity and the reference to the

play11:41

new

play11:43

array this involves copying all the

play11:45

values from the previous array to the

play11:47

new one once the elements are copied to

play11:50

the new array the previous array is no

play11:52

longer needed so we free its

play11:54

memory finally with free space available

play11:57

in the new array we can add the new

play11:58

element and update the length

play12:02

accordingly and that's it now we have a

play12:04

list of elements whose size can grow

play12:07

dynamically and please don't tell me in

play12:09

the comments that this code can be

play12:10

written in better ways it was written by

play12:12

my cousin who knows more than you now

play12:15

it's important to mention that this

play12:17

process of creating a new larger array

play12:19

when the current one is full involves a

play12:21

heap allocation which can have

play12:23

performance implications as we discussed

play12:25

in a previous

play12:26

episode additionally copying elements

play12:29

around in memory also incurs additional

play12:31

time if you know the approximate or

play12:33

exact number of elements the array list

play12:35

will contain and you're concerned about

play12:37

performance it might be wise to create

play12:39

the array list with a specific initial

play12:42

capacity this can reduce the need for

play12:44

frequent resizing operations which means

play12:46

fewer allocations which in turn means

play12:48

better

play12:52

performance this is why in languages

play12:54

like rust you usually create a vector

play12:56

the equivalent of an array list using

play12:58

the new method but there's also the WID

play13:00

capacity method

play13:04

available another characteristic that

play13:06

array lists inherit from arrays is

play13:08

constant time access since elements are

play13:10

internally stored in an array accessing

play13:13

an element at a specific position simply

play13:15

involves retrieving the element from the

play13:16

array by indexing it this operation has

play13:19

a constant time

play13:21

complexity to prevent accessing elements

play13:24

beyond the array boundaries we perform a

play13:26

validation before indexing the array

play13:29

this way if the provided index is

play13:31

greater than or equal to the array

play13:32

length we can return null none or a

play13:35

proper

play13:36

error this effectively solves the

play13:39

problem of accessing outof bound

play13:40

elements causing undefined behaviors

play13:42

which arrays by themselves cannot avoid

play13:45

though it adds an extra step the time

play13:47

complexity remains

play13:50

constant the reason I'm bringing this up

play13:52

is because in linked lists accessing

play13:54

elements is not that easy unlike arrays

play13:57

linked lists don't provide direct access

play13:59

to Elements by index as elements are

play14:02

scattered in memory there is no formula

play14:04

to determine the addresses where

play14:05

elements

play14:07

reside accessing an element at a

play14:09

specific position in a linked list

play14:11

requires traversing the list from the

play14:13

head until reaching the node in the

play14:15

desired position since each node must be

play14:18

traversed individually the time

play14:20

complexity for indexing in quotes a

play14:22

linked list is

play14:24

linear the time complexity is not the

play14:27

only performance challenge here even if

play14:29

the desired element is in the cache the

play14:31

other nodes along the path to find that

play14:33

element are often not cached which can

play14:35

significantly slow down the traversing

play14:38

process when is up to remove operations

play14:41

after deleting an element from the array

play14:43

all the subsequent elements must be

play14:45

shifted back one position in the array

play14:47

so keep in mind that copying data in

play14:49

memory can be timec

play14:51

consuming if the removed element is

play14:53

closer to the end of the array the

play14:55

shifting process is faster since there

play14:57

are fewer elements to shift

play14:59

if the removed element is closer to the

play15:01

beginning of the array there are more

play15:03

elements that need to be shifted which

play15:04

can significantly slow down the

play15:07

process of course this also depends on

play15:09

the number of elements within the array

play15:14

list now linked lists Fanatics would

play15:16

argue that remove operations are faster

play15:19

because everything we must do to remove

play15:20

an element is to update its previous

play15:22

node to point to the following node and

play15:24

then free The Unwanted nodes

play15:26

memory and yes it is true as no shifting

play15:30

operations are needed the time

play15:32

complexity of this operation is constant

play15:34

but we also should consider that in

play15:36

order to update this node we first need

play15:38

to find its value in memory and as I've

play15:41

mentioned like 20,000 times accessing an

play15:43

element in a linked list is not

play15:45

efficient due to caching but before

play15:47

putting shame on linked lists as we're

play15:49

going to do with JavaScript in a moment

play15:51

consider that contrary to what is

play15:53

usually said linked list could still be

play15:55

a better choice in certain

play15:56

situations and listen correctly please

play15:59

I said it could not all architectures

play16:01

base their efficiency on caches nor in

play16:04

data shifting instructions I'll read

play16:06

your comments about your opinions on

play16:08

this matter at the end of the day

play16:10

picking the right data structure depends

play16:12

on several factors when you get into the

play16:14

low-level World you'll usually find

play16:16

yourself benchmarking multiple Solutions

play16:19

attempting to ring out every drop of

play16:20

efficiency from the

play16:22

hardware now before getting into the

play16:24

final part you might have noticed that

play16:26

in both cases we created the data

play16:28

structure for a specific specific kind

play16:29

of element here the array list Can Only

play16:32

Hold 16bit signed integers while the

play16:34

linked list Can Only Hold 8 bit unsigned

play16:37

integers so how can we solve this

play16:40

without having to create a different

play16:41

version for each type we

play16:43

desire well if you are thinking about

play16:45

generic types yes you are right but just

play16:49

to clarify generics don't work the same

play16:51

way in all programming languages in Rust

play16:54

for example when you declare a generic

play16:56

type the compiler identifies each

play16:58

instance where you use that generic

play17:01

generates source code specific to that

play17:03

type and substitutes the generic type

play17:05

with the generated

play17:07

type this process is invisible to us as

play17:10

it's accomplished at compile time as a

play17:13

result in memory we get data structured

play17:15

exactly as we

play17:21

expected another approach is employed by

play17:23

Java for custom types if the type

play17:26

contained by the array is not primitive

play17:28

in instead of creating an array of

play17:30

values the VM is actually creating an

play17:32

array of pointers pointing to the memory

play17:34

addresses where the elements resides in

play17:36

memory here the language is designed to

play17:38

hide all of those pointers from us so we

play17:41

think we are dealing with arrays of

play17:42

values when in reality we are

play17:45

not if you are thinking this is bad for

play17:48

caching yes this is one of the reasons

play17:51

why Java is considered a pretty fast

play17:52

language but still slower than systems

play17:54

programming

play17:56

languages with this approach linked

play17:58

lists can get even worse because nodes

play18:00

won't contain the actual element and the

play18:02

pointer to the next node but a pointer

play18:04

to the element and a pointer to the next

play18:06

node a cache nightmare one that

play18:09

hilariously is very common to see in

play18:11

languages like C where beginners are

play18:14

tempted to create something like this

play18:15

due to the lack of

play18:17

generics and this leads us to the final

play18:19

part of this video how do scripting

play18:21

languages handle

play18:23

arrays all right let's quickly examine

play18:26

both the JavaScript and python

play18:27

approaches the first thing to remember

play18:30

is that scripting languages are

play18:31

typically not compiled into machine code

play18:34

instead they rely on a special program

play18:36

to interpret the script allowing it to

play18:38

mimic or emulate the desired

play18:40

behavior of course to execute that

play18:42

emulation process the real Hardware is

play18:45

used for this reason such programs are

play18:48

often referred to as interpreters or

play18:50

virtual machines when we declare an

play18:53

array behind the scenes The Interpreter

play18:55

generates a specialized data structure

play18:57

to simulate an array of elements ments

play18:59

to understand what python does we can

play19:02

explore the implementation of the

play19:03

official interpreter which is open

play19:05

source here we encounter a type called

play19:08

py list object this is what The

play19:10

Interpreter generates when we create a

play19:12

python list the documentation comment

play19:14

here states that this type is nothing

play19:16

but an array of object

play19:18

pointers so it's similar to what Java

play19:20

does for non-primitive types an array of

play19:22

memory addresses pointing to the values

play19:25

but there's a key difference here in

play19:27

Java all pointers with within the array

play19:29

point to values of the same type in

play19:31

Python these pointers point to objects

play19:34

implying they can point to values of

play19:35

different types this explains us being

play19:38

able to create an array and still being

play19:40

able to add multiple kind of elements to

play19:42

it because all of these pointers are of

play19:45

the same size when we index a python

play19:47

list The Interpreter simulates indexing

play19:49

the list by first indexing the array of

play19:51

pointers and then reading the value that

play19:53

the pointer is pointing

play19:56

to and finally let's talk about about

play19:59

JavaScript in simple terms JavaScript

play20:01

arrays aren't actually arrays they're

play20:05

hashmaps JavaScript doesn't concern

play20:07

itself much with arrays in memory

play20:09

instead when a JavaScript array is

play20:11

declared the engine creates a hashmap

play20:13

where the keys correspond to the

play20:16

indexes you can verify this yourself

play20:18

create a Javascript file declare an

play20:20

empty array and then insert some

play20:22

elements at the position hello as you'll

play20:25

observe the indexing process is

play20:27

essentially syntax sugar for retrieving

play20:29

an element from the hashmap by its key

play20:32

now even though a lot of people claim

play20:33

that I don't like JavaScript I have to

play20:35

admit that this is a very bizarre but

play20:37

clever solution what's particularly

play20:40

fascinating about this is that if we add

play20:42

an element at position 1 million the

play20:44

engine won't need to allocate memory for

play20:46

an array that large instead it simply

play20:49

inserts another element into the table

play20:51

accessible with the key 1 million very

play20:55

cool huh and that about wraps for now if

play20:57

you notice that the anim quality

play20:59

decreases in the JavaScript part it's

play21:01

not a coincidence this video was

play21:03

particularly hard to animate so hit the

play21:05

like button if you learned something and

play21:07

if you want to learn more consider

play21:09

subscribing and following me in other

play21:13

platforms the subscriber count has

play21:15

double since the last video so again

play21:18

thanks a lot for your support

Rate This
β˜…
β˜…
β˜…
β˜…
β˜…

5.0 / 5 (0 votes)

Related Tags
ArraysProgrammingJavaScriptPythonData StructuresMemory ManagementCachingPerformanceSystems ProgrammingScripting Languages