For anyone who's interested, here's an implementation with the cards represented as integers (or wrapping integers). Perhaps this could be the basis of a super-compact representation. In any case, sorting hands becomes simpler, but either dealing (creating a card object) or checking that a play is legal becomes more involved (because at some point you would have to reconstruct the symbolic information regarding suit and rank).

Alternatively, if you want to make the deck bridge-specific you could change the undealt variable into an Array or OrderedCollection, and after shuffling treat each of the quarters as belonging to a specific hand. Again, more compact, less simplicity. Taking something out of the hand could be represented by replacing that card's integer with 0 in the deck/hand array.

Another alternative would be to use an "ordinary" representation for shuffling etc. then write that into a bit-packed representation with each two bits representing a player, as the original questioner wanted. Some utility classes could go over that to make handling such a thing look like an array of players, and use a formatting method similar to the one used in the attached code.